├── .gitignore ├── .vscode └── settings.json ├── index.ts ├── package.json ├── readme.md ├── tsconfig.json ├── utils.ts └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | uploads/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "./node_modules/typescript/lib" 3 | } -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import * as express from 'express' 2 | import * as multer from 'multer' 3 | import * as cors from 'cors' 4 | import * as fs from 'fs' 5 | import * as path from 'path' 6 | import * as Loki from 'lokijs' 7 | import { imageFilter, loadCollection, cleanFolder } from './utils'; 8 | 9 | // setup 10 | const DB_NAME = 'db.json'; 11 | const COLLECTION_NAME = 'images'; 12 | const UPLOAD_PATH = 'uploads'; 13 | const upload = multer({ dest: `${UPLOAD_PATH}/`, fileFilter: imageFilter }); 14 | const db = new Loki(`${UPLOAD_PATH}/${DB_NAME}`, { persistenceMethod: 'fs' }); 15 | 16 | // optional: clean all data before start 17 | // cleanFolder(UPLOAD_PATH); 18 | 19 | // app 20 | const app = express(); 21 | app.use(cors()); 22 | 23 | app.get('/', async (req, res) => { 24 | // default route 25 | res.send(` 26 |

Demo file upload

27 |

Please refer to my tutorial for details.

28 | 34 | `); 35 | }) 36 | 37 | app.post('/profile', upload.single('avatar'), async (req, res) => { 38 | try { 39 | const col = await loadCollection(COLLECTION_NAME, db); 40 | const data = col.insert(req.file); 41 | 42 | db.saveDatabase(); 43 | res.send({ id: data.$loki, fileName: data.filename, originalName: data.originalname }); 44 | } catch (err) { 45 | res.sendStatus(400); 46 | } 47 | }) 48 | 49 | app.post('/photos/upload', upload.array('photos', 12), async (req, res) => { 50 | try { 51 | const col = await loadCollection(COLLECTION_NAME, db) 52 | let data = [].concat(col.insert(req.files)); 53 | 54 | db.saveDatabase(); 55 | res.send(data.map(x => ({ id: x.$loki, fileName: x.filename, originalName: x.originalname }))); 56 | } catch (err) { 57 | res.sendStatus(400); 58 | } 59 | }) 60 | 61 | app.get('/images', async (req, res) => { 62 | try { 63 | const col = await loadCollection(COLLECTION_NAME, db); 64 | res.send(col.data); 65 | } catch (err) { 66 | res.sendStatus(400); 67 | } 68 | }) 69 | 70 | app.get('/images/:id', async (req, res) => { 71 | try { 72 | const col = await loadCollection(COLLECTION_NAME, db); 73 | const result = col.get(req.params.id); 74 | 75 | if (!result) { 76 | res.sendStatus(404); 77 | return; 78 | }; 79 | 80 | res.setHeader('Content-Type', result.mimetype); 81 | fs.createReadStream(path.join(UPLOAD_PATH, result.filename)).pipe(res); 82 | } catch (err) { 83 | res.sendStatus(400); 84 | } 85 | }) 86 | 87 | app.listen(3000, function () { 88 | console.log('listening on port 3000!'); 89 | }) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "file-upload-express", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "prestart": "tsc", 8 | "start": "node dist/index.js" 9 | }, 10 | "dependencies": { 11 | "cors": "^2.8.4", 12 | "del": "^3.0.0", 13 | "express": "^4.16.3", 14 | "lokijs": "^1.5.3", 15 | "multer": "^1.3.0" 16 | }, 17 | "devDependencies": { 18 | "@types/del": "^3.0.1", 19 | "@types/express": "^4.11.1", 20 | "@types/lokijs": "^1.5.2", 21 | "@types/multer": "^1.3.6", 22 | "typescript": "^2.8.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # File upload with Node, Express, Mutler, Typescript 2 | 3 | This is the repo for tutorial: https://scotch.io/tutorials/express-file-uploads-with-multer. 4 | 5 | 1. Install [nodejs](https://nodejs.org/en/) (version 7.5+) and [yarn](https://yarnpkg.com/en/docs/install). 6 | 2. Go to project directory, run `yarn`. 7 | 3. Start the application, run `yarn start`. 8 | 4. Go to `localhost:3000` 9 | 10 | ## API End Points 11 | 12 | 1. Upload an image via `localhost:3000/profile`, avatar field. 13 | 2. Bulk Upload images via `localhost:3000/photos/upload`, photos field. 14 | 3. View list of images via `localhost:3000/images`. 15 | 4. Get a single image via `localhost:3000/images/{imageId}`. 16 | 17 | # License 18 | [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](/LICENSE) 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "moduleResolution": "node", 5 | "target": "es6", 6 | "noImplicitAny": false, 7 | "sourceMap": true, 8 | "outDir": "dist" 9 | } 10 | } -------------------------------------------------------------------------------- /utils.ts: -------------------------------------------------------------------------------- 1 | import * as del from 'del'; 2 | import { Collection } from 'lokijs'; 3 | 4 | const imageFilter = function (req, file, cb) { 5 | // accept image only 6 | if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) { 7 | return cb(new Error('Only image files are allowed!'), false); 8 | } 9 | cb(null, true); 10 | }; 11 | 12 | const loadCollection = function (colName, db: Loki): Promise> { 13 | return new Promise(resolve => { 14 | db.loadDatabase({}, () => { 15 | const _collection = db.getCollection(colName) || db.addCollection(colName); 16 | resolve(_collection); 17 | }) 18 | }); 19 | } 20 | 21 | const cleanFolder = function (folderPath) { 22 | // delete files inside folder but not the folder itself 23 | del.sync([`${folderPath}/**`, `!${folderPath}`]); 24 | }; 25 | 26 | export { imageFilter, loadCollection, cleanFolder } 27 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/body-parser@*": 6 | version "1.16.8" 7 | resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.16.8.tgz#687ec34140624a3bec2b1a8ea9268478ae8f3be3" 8 | dependencies: 9 | "@types/express" "*" 10 | "@types/node" "*" 11 | 12 | "@types/del@^3.0.1": 13 | version "3.0.1" 14 | resolved "https://registry.yarnpkg.com/@types/del/-/del-3.0.1.tgz#4712da8c119873cbbf533ad8dbf1baac5940ac5d" 15 | dependencies: 16 | "@types/glob" "*" 17 | 18 | "@types/express-serve-static-core@*": 19 | version "4.0.40" 20 | resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.0.40.tgz#168e82978bffc81ee7737bc60728d64733a4f37b" 21 | dependencies: 22 | "@types/node" "*" 23 | 24 | "@types/express@*": 25 | version "4.0.35" 26 | resolved "https://registry.yarnpkg.com/@types/express/-/express-4.0.35.tgz#6267c7b60a51fac473467b3c4a02cd1e441805fe" 27 | dependencies: 28 | "@types/express-serve-static-core" "*" 29 | "@types/serve-static" "*" 30 | 31 | "@types/express@^4.11.1": 32 | version "4.11.1" 33 | resolved "https://registry.yarnpkg.com/@types/express/-/express-4.11.1.tgz#f99663b3ab32d04cb11db612ef5dd7933f75465b" 34 | dependencies: 35 | "@types/body-parser" "*" 36 | "@types/express-serve-static-core" "*" 37 | "@types/serve-static" "*" 38 | 39 | "@types/glob@*": 40 | version "5.0.30" 41 | resolved "https://registry.yarnpkg.com/@types/glob/-/glob-5.0.30.tgz#1026409c5625a8689074602808d082b2867b8a51" 42 | dependencies: 43 | "@types/minimatch" "*" 44 | "@types/node" "*" 45 | 46 | "@types/lokijs@^1.5.2": 47 | version "1.5.2" 48 | resolved "https://registry.yarnpkg.com/@types/lokijs/-/lokijs-1.5.2.tgz#ed228f080033ce1fb16eff4acde65cb9ae0f1bf2" 49 | 50 | "@types/mime@*": 51 | version "0.0.29" 52 | resolved "https://registry.yarnpkg.com/@types/mime/-/mime-0.0.29.tgz#fbcfd330573b912ef59eeee14602bface630754b" 53 | 54 | "@types/minimatch@*": 55 | version "2.0.29" 56 | resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-2.0.29.tgz#5002e14f75e2d71e564281df0431c8c1b4a2a36a" 57 | 58 | "@types/multer@^1.3.6": 59 | version "1.3.6" 60 | resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.3.6.tgz#e00454074bf9fd86d20ea652eba9850eb76604c4" 61 | dependencies: 62 | "@types/express" "*" 63 | 64 | "@types/node@*": 65 | version "7.0.5" 66 | resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.5.tgz#96a0f0a618b7b606f1ec547403c00650210bfbb7" 67 | 68 | "@types/serve-static@*": 69 | version "1.7.31" 70 | resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.7.31.tgz#15456de8d98d6b4cff31be6c6af7492ae63f521a" 71 | dependencies: 72 | "@types/express-serve-static-core" "*" 73 | "@types/mime" "*" 74 | 75 | accepts@~1.3.5: 76 | version "1.3.5" 77 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" 78 | dependencies: 79 | mime-types "~2.1.18" 80 | negotiator "0.6.1" 81 | 82 | append-field@^0.1.0: 83 | version "0.1.0" 84 | resolved "https://registry.yarnpkg.com/append-field/-/append-field-0.1.0.tgz#6ddc58fa083c7bc545d3c5995b2830cc2366d44a" 85 | 86 | array-flatten@1.1.1: 87 | version "1.1.1" 88 | resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" 89 | 90 | array-union@^1.0.1: 91 | version "1.0.2" 92 | resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" 93 | dependencies: 94 | array-uniq "^1.0.1" 95 | 96 | array-uniq@^1.0.1: 97 | version "1.0.3" 98 | resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" 99 | 100 | balanced-match@^0.4.1: 101 | version "0.4.2" 102 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" 103 | 104 | body-parser@1.18.2: 105 | version "1.18.2" 106 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454" 107 | dependencies: 108 | bytes "3.0.0" 109 | content-type "~1.0.4" 110 | debug "2.6.9" 111 | depd "~1.1.1" 112 | http-errors "~1.6.2" 113 | iconv-lite "0.4.19" 114 | on-finished "~2.3.0" 115 | qs "6.5.1" 116 | raw-body "2.3.2" 117 | type-is "~1.6.15" 118 | 119 | brace-expansion@^1.0.0: 120 | version "1.1.6" 121 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" 122 | dependencies: 123 | balanced-match "^0.4.1" 124 | concat-map "0.0.1" 125 | 126 | buffer-shims@^1.0.0: 127 | version "1.0.0" 128 | resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" 129 | 130 | busboy@^0.2.11: 131 | version "0.2.14" 132 | resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" 133 | dependencies: 134 | dicer "0.2.5" 135 | readable-stream "1.1.x" 136 | 137 | bytes@3.0.0: 138 | version "3.0.0" 139 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" 140 | 141 | concat-map@0.0.1: 142 | version "0.0.1" 143 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 144 | 145 | concat-stream@^1.5.0: 146 | version "1.6.0" 147 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" 148 | dependencies: 149 | inherits "^2.0.3" 150 | readable-stream "^2.2.2" 151 | typedarray "^0.0.6" 152 | 153 | content-disposition@0.5.2: 154 | version "0.5.2" 155 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" 156 | 157 | content-type@~1.0.4: 158 | version "1.0.4" 159 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" 160 | 161 | cookie-signature@1.0.6: 162 | version "1.0.6" 163 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" 164 | 165 | cookie@0.3.1: 166 | version "0.3.1" 167 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" 168 | 169 | core-util-is@~1.0.0: 170 | version "1.0.2" 171 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 172 | 173 | cors@^2.8.4: 174 | version "2.8.4" 175 | resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.4.tgz#2bd381f2eb201020105cd50ea59da63090694686" 176 | dependencies: 177 | object-assign "^4" 178 | vary "^1" 179 | 180 | debug@2.6.9: 181 | version "2.6.9" 182 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 183 | dependencies: 184 | ms "2.0.0" 185 | 186 | del@^3.0.0: 187 | version "3.0.0" 188 | resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5" 189 | dependencies: 190 | globby "^6.1.0" 191 | is-path-cwd "^1.0.0" 192 | is-path-in-cwd "^1.0.0" 193 | p-map "^1.1.1" 194 | pify "^3.0.0" 195 | rimraf "^2.2.8" 196 | 197 | depd@1.1.1: 198 | version "1.1.1" 199 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" 200 | 201 | depd@~1.1.1, depd@~1.1.2: 202 | version "1.1.2" 203 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" 204 | 205 | destroy@~1.0.4: 206 | version "1.0.4" 207 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" 208 | 209 | dicer@0.2.5: 210 | version "0.2.5" 211 | resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f" 212 | dependencies: 213 | readable-stream "1.1.x" 214 | streamsearch "0.1.2" 215 | 216 | ee-first@1.1.1: 217 | version "1.1.1" 218 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 219 | 220 | encodeurl@~1.0.2: 221 | version "1.0.2" 222 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" 223 | 224 | escape-html@~1.0.3: 225 | version "1.0.3" 226 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 227 | 228 | etag@~1.8.1: 229 | version "1.8.1" 230 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 231 | 232 | express@^4.16.3: 233 | version "4.16.3" 234 | resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53" 235 | dependencies: 236 | accepts "~1.3.5" 237 | array-flatten "1.1.1" 238 | body-parser "1.18.2" 239 | content-disposition "0.5.2" 240 | content-type "~1.0.4" 241 | cookie "0.3.1" 242 | cookie-signature "1.0.6" 243 | debug "2.6.9" 244 | depd "~1.1.2" 245 | encodeurl "~1.0.2" 246 | escape-html "~1.0.3" 247 | etag "~1.8.1" 248 | finalhandler "1.1.1" 249 | fresh "0.5.2" 250 | merge-descriptors "1.0.1" 251 | methods "~1.1.2" 252 | on-finished "~2.3.0" 253 | parseurl "~1.3.2" 254 | path-to-regexp "0.1.7" 255 | proxy-addr "~2.0.3" 256 | qs "6.5.1" 257 | range-parser "~1.2.0" 258 | safe-buffer "5.1.1" 259 | send "0.16.2" 260 | serve-static "1.13.2" 261 | setprototypeof "1.1.0" 262 | statuses "~1.4.0" 263 | type-is "~1.6.16" 264 | utils-merge "1.0.1" 265 | vary "~1.1.2" 266 | 267 | finalhandler@1.1.1: 268 | version "1.1.1" 269 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" 270 | dependencies: 271 | debug "2.6.9" 272 | encodeurl "~1.0.2" 273 | escape-html "~1.0.3" 274 | on-finished "~2.3.0" 275 | parseurl "~1.3.2" 276 | statuses "~1.4.0" 277 | unpipe "~1.0.0" 278 | 279 | forwarded@~0.1.2: 280 | version "0.1.2" 281 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" 282 | 283 | fresh@0.5.2: 284 | version "0.5.2" 285 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" 286 | 287 | fs.realpath@^1.0.0: 288 | version "1.0.0" 289 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 290 | 291 | glob@^7.0.3, glob@^7.0.5: 292 | version "7.1.1" 293 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" 294 | dependencies: 295 | fs.realpath "^1.0.0" 296 | inflight "^1.0.4" 297 | inherits "2" 298 | minimatch "^3.0.2" 299 | once "^1.3.0" 300 | path-is-absolute "^1.0.0" 301 | 302 | globby@^6.1.0: 303 | version "6.1.0" 304 | resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" 305 | dependencies: 306 | array-union "^1.0.1" 307 | glob "^7.0.3" 308 | object-assign "^4.0.1" 309 | pify "^2.0.0" 310 | pinkie-promise "^2.0.0" 311 | 312 | http-errors@1.6.2: 313 | version "1.6.2" 314 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" 315 | dependencies: 316 | depd "1.1.1" 317 | inherits "2.0.3" 318 | setprototypeof "1.0.3" 319 | statuses ">= 1.3.1 < 2" 320 | 321 | http-errors@~1.6.2: 322 | version "1.6.3" 323 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" 324 | dependencies: 325 | depd "~1.1.2" 326 | inherits "2.0.3" 327 | setprototypeof "1.1.0" 328 | statuses ">= 1.4.0 < 2" 329 | 330 | iconv-lite@0.4.19: 331 | version "0.4.19" 332 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" 333 | 334 | inflight@^1.0.4: 335 | version "1.0.6" 336 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 337 | dependencies: 338 | once "^1.3.0" 339 | wrappy "1" 340 | 341 | inherits@2, inherits@2.0.3, inherits@^2.0.3, inherits@~2.0.1: 342 | version "2.0.3" 343 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 344 | 345 | ipaddr.js@1.6.0: 346 | version "1.6.0" 347 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b" 348 | 349 | is-path-cwd@^1.0.0: 350 | version "1.0.0" 351 | resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" 352 | 353 | is-path-in-cwd@^1.0.0: 354 | version "1.0.0" 355 | resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" 356 | dependencies: 357 | is-path-inside "^1.0.0" 358 | 359 | is-path-inside@^1.0.0: 360 | version "1.0.0" 361 | resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" 362 | dependencies: 363 | path-is-inside "^1.0.1" 364 | 365 | isarray@0.0.1: 366 | version "0.0.1" 367 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" 368 | 369 | isarray@~1.0.0: 370 | version "1.0.0" 371 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 372 | 373 | lokijs@^1.5.3: 374 | version "1.5.3" 375 | resolved "https://registry.yarnpkg.com/lokijs/-/lokijs-1.5.3.tgz#6952722ffa3049a55a5e1c10ee4a0947a3e5e19b" 376 | 377 | media-typer@0.3.0: 378 | version "0.3.0" 379 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 380 | 381 | merge-descriptors@1.0.1: 382 | version "1.0.1" 383 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 384 | 385 | methods@~1.1.2: 386 | version "1.1.2" 387 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 388 | 389 | mime-db@~1.26.0: 390 | version "1.26.0" 391 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.26.0.tgz#eaffcd0e4fc6935cf8134da246e2e6c35305adff" 392 | 393 | mime-db@~1.33.0: 394 | version "1.33.0" 395 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" 396 | 397 | mime-types@~2.1.13: 398 | version "2.1.14" 399 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.14.tgz#f7ef7d97583fcaf3b7d282b6f8b5679dab1e94ee" 400 | dependencies: 401 | mime-db "~1.26.0" 402 | 403 | mime-types@~2.1.18: 404 | version "2.1.18" 405 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" 406 | dependencies: 407 | mime-db "~1.33.0" 408 | 409 | mime@1.4.1: 410 | version "1.4.1" 411 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" 412 | 413 | minimatch@^3.0.2: 414 | version "3.0.3" 415 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" 416 | dependencies: 417 | brace-expansion "^1.0.0" 418 | 419 | minimist@0.0.8: 420 | version "0.0.8" 421 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 422 | 423 | mkdirp@^0.5.1: 424 | version "0.5.1" 425 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 426 | dependencies: 427 | minimist "0.0.8" 428 | 429 | ms@2.0.0: 430 | version "2.0.0" 431 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 432 | 433 | multer@^1.3.0: 434 | version "1.3.0" 435 | resolved "https://registry.yarnpkg.com/multer/-/multer-1.3.0.tgz#092b2670f6846fa4914965efc8cf94c20fec6cd2" 436 | dependencies: 437 | append-field "^0.1.0" 438 | busboy "^0.2.11" 439 | concat-stream "^1.5.0" 440 | mkdirp "^0.5.1" 441 | object-assign "^3.0.0" 442 | on-finished "^2.3.0" 443 | type-is "^1.6.4" 444 | xtend "^4.0.0" 445 | 446 | negotiator@0.6.1: 447 | version "0.6.1" 448 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" 449 | 450 | object-assign@^3.0.0: 451 | version "3.0.0" 452 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" 453 | 454 | object-assign@^4, object-assign@^4.0.1: 455 | version "4.1.1" 456 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 457 | 458 | on-finished@^2.3.0, on-finished@~2.3.0: 459 | version "2.3.0" 460 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 461 | dependencies: 462 | ee-first "1.1.1" 463 | 464 | once@^1.3.0: 465 | version "1.4.0" 466 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 467 | dependencies: 468 | wrappy "1" 469 | 470 | p-map@^1.1.1: 471 | version "1.2.0" 472 | resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" 473 | 474 | parseurl@~1.3.2: 475 | version "1.3.2" 476 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" 477 | 478 | path-is-absolute@^1.0.0: 479 | version "1.0.1" 480 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 481 | 482 | path-is-inside@^1.0.1: 483 | version "1.0.2" 484 | resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" 485 | 486 | path-to-regexp@0.1.7: 487 | version "0.1.7" 488 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" 489 | 490 | pify@^2.0.0: 491 | version "2.3.0" 492 | resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" 493 | 494 | pify@^3.0.0: 495 | version "3.0.0" 496 | resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" 497 | 498 | pinkie-promise@^2.0.0: 499 | version "2.0.1" 500 | resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" 501 | dependencies: 502 | pinkie "^2.0.0" 503 | 504 | pinkie@^2.0.0: 505 | version "2.0.4" 506 | resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" 507 | 508 | process-nextick-args@~1.0.6: 509 | version "1.0.7" 510 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" 511 | 512 | proxy-addr@~2.0.3: 513 | version "2.0.3" 514 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341" 515 | dependencies: 516 | forwarded "~0.1.2" 517 | ipaddr.js "1.6.0" 518 | 519 | qs@6.5.1: 520 | version "6.5.1" 521 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" 522 | 523 | range-parser@~1.2.0: 524 | version "1.2.0" 525 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" 526 | 527 | raw-body@2.3.2: 528 | version "2.3.2" 529 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" 530 | dependencies: 531 | bytes "3.0.0" 532 | http-errors "1.6.2" 533 | iconv-lite "0.4.19" 534 | unpipe "1.0.0" 535 | 536 | readable-stream@1.1.x: 537 | version "1.1.14" 538 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" 539 | dependencies: 540 | core-util-is "~1.0.0" 541 | inherits "~2.0.1" 542 | isarray "0.0.1" 543 | string_decoder "~0.10.x" 544 | 545 | readable-stream@^2.2.2: 546 | version "2.2.2" 547 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e" 548 | dependencies: 549 | buffer-shims "^1.0.0" 550 | core-util-is "~1.0.0" 551 | inherits "~2.0.1" 552 | isarray "~1.0.0" 553 | process-nextick-args "~1.0.6" 554 | string_decoder "~0.10.x" 555 | util-deprecate "~1.0.1" 556 | 557 | rimraf@^2.2.8: 558 | version "2.5.4" 559 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" 560 | dependencies: 561 | glob "^7.0.5" 562 | 563 | safe-buffer@5.1.1: 564 | version "5.1.1" 565 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" 566 | 567 | send@0.16.2: 568 | version "0.16.2" 569 | resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" 570 | dependencies: 571 | debug "2.6.9" 572 | depd "~1.1.2" 573 | destroy "~1.0.4" 574 | encodeurl "~1.0.2" 575 | escape-html "~1.0.3" 576 | etag "~1.8.1" 577 | fresh "0.5.2" 578 | http-errors "~1.6.2" 579 | mime "1.4.1" 580 | ms "2.0.0" 581 | on-finished "~2.3.0" 582 | range-parser "~1.2.0" 583 | statuses "~1.4.0" 584 | 585 | serve-static@1.13.2: 586 | version "1.13.2" 587 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" 588 | dependencies: 589 | encodeurl "~1.0.2" 590 | escape-html "~1.0.3" 591 | parseurl "~1.3.2" 592 | send "0.16.2" 593 | 594 | setprototypeof@1.0.3: 595 | version "1.0.3" 596 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" 597 | 598 | setprototypeof@1.1.0: 599 | version "1.1.0" 600 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" 601 | 602 | "statuses@>= 1.3.1 < 2": 603 | version "1.3.1" 604 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" 605 | 606 | "statuses@>= 1.4.0 < 2": 607 | version "1.5.0" 608 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" 609 | 610 | statuses@~1.4.0: 611 | version "1.4.0" 612 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" 613 | 614 | streamsearch@0.1.2: 615 | version "0.1.2" 616 | resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" 617 | 618 | string_decoder@~0.10.x: 619 | version "0.10.31" 620 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" 621 | 622 | type-is@^1.6.4: 623 | version "1.6.14" 624 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.14.tgz#e219639c17ded1ca0789092dd54a03826b817cb2" 625 | dependencies: 626 | media-typer "0.3.0" 627 | mime-types "~2.1.13" 628 | 629 | type-is@~1.6.15, type-is@~1.6.16: 630 | version "1.6.16" 631 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" 632 | dependencies: 633 | media-typer "0.3.0" 634 | mime-types "~2.1.18" 635 | 636 | typedarray@^0.0.6: 637 | version "0.0.6" 638 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 639 | 640 | typescript@^2.8.1: 641 | version "2.8.1" 642 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.8.1.tgz#6160e4f8f195d5ba81d4876f9c0cc1fbc0820624" 643 | 644 | unpipe@1.0.0, unpipe@~1.0.0: 645 | version "1.0.0" 646 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 647 | 648 | util-deprecate@~1.0.1: 649 | version "1.0.2" 650 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 651 | 652 | utils-merge@1.0.1: 653 | version "1.0.1" 654 | resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" 655 | 656 | vary@^1: 657 | version "1.1.0" 658 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.0.tgz#e1e5affbbd16ae768dd2674394b9ad3022653140" 659 | 660 | vary@~1.1.2: 661 | version "1.1.2" 662 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" 663 | 664 | wrappy@1: 665 | version "1.0.2" 666 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 667 | 668 | xtend@^4.0.0: 669 | version "4.0.1" 670 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" 671 | --------------------------------------------------------------------------------