From 142f4dd92d0f941b72fe5f053be2331e5667ad65 Mon Sep 17 00:00:00 2001 From: Jerome Wu Date: Mon, 30 Sep 2019 11:07:40 +0800 Subject: [PATCH] Refactor core apis --- examples/node/recognize.js | 27 +- package-lock.json | 856 +--------------------------------- src/common/createJob.js | 27 ++ src/common/createScheduler.js | 57 +++ src/common/createTesseract.js | 7 + src/common/createWorker.js | 129 +++++ src/common/env.js | 1 + src/common/options.js | 1 + src/common/utils.js | 87 ++++ src/common/workerUtils.js | 343 +++++--------- src/index.js | 6 + src/node/index.js | 36 +- 12 files changed, 470 insertions(+), 1107 deletions(-) create mode 100644 src/common/createJob.js create mode 100644 src/common/createScheduler.js create mode 100644 src/common/createTesseract.js create mode 100644 src/common/createWorker.js create mode 100644 src/common/env.js create mode 100644 src/common/utils.js diff --git a/examples/node/recognize.js b/examples/node/recognize.js index d283944..804820a 100755 --- a/examples/node/recognize.js +++ b/examples/node/recognize.js @@ -1,23 +1,22 @@ #!/usr/bin/env node const path = require('path'); -const { TesseractWorker } = require('../../'); +const { createScheduler, createWorker, createJob, OEM } = require('../../'); const [,, imagePath] = process.argv; const image = path.resolve(__dirname, (imagePath || '../../tests/assets/images/cosmic.png')); -const tessWorker = new TesseractWorker(); console.log(`Recognizing ${image}`); -tessWorker.recognize(image) - .progress((info) => { - console.log(info); - }) - .then((data) => { - console.log(data.text); - }) - .catch((err) => { - console.log('Error\n', err); - }) - .finally(() => { - process.exit(); +(async () => { + const scheduler = createScheduler(); + const worker = createWorker(); + await worker.load(); + await worker.loadLanguage('osd'); + await worker.initialize('osd', { + tessedit_ocr_engine_mode: OEM.OSD_ONLY, }); + scheduler.addWorker(worker); + const data = await scheduler.addJob(createJob('detect', { image })); + console.log(data); + scheduler.terminate(); +})(); diff --git a/package-lock.json b/package-lock.json index 4c2b0b1..a6bc2ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -214,22 +214,22 @@ } }, "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz", + "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==", "dev": true }, "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz", + "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "@babel/generator": "^7.4.4", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", + "@babel/parser": "^7.4.5", "@babel/types": "^7.4.4", "debug": "^4.1.0", "globals": "^11.1.0", @@ -376,848 +376,6 @@ "@babel/types": "^7.0.0" } }, - "@babel/helper-module-transforms": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.4.4.tgz", - "integrity": "sha512-3Z1yp8TVQf+B4ynN7WoHPKS8EkdTbgAEy0nU0rs/1Kw4pDgmvYH3rz3aI11KgxKCba2cn7N+tqzV1mY2HMN96w==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/template": "^7.4.4", - "@babel/types": "^7.4.4", - "lodash": "^4.17.11" - }, - "dependencies": { - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", - "dev": true - }, - "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - } - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz", - "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", - "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", - "dev": true - }, - "@babel/helper-regex": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.4.4.tgz", - "integrity": "sha512-Y5nuB/kESmR3tKjU8Nkn1wMGEx1tjJX076HBMeL3XLQCu6vA/YRzuTW0bbb+qRnXvQGn+d6Rx953yffl8vEy7Q==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - }, - "dependencies": { - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - } - } - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz", - "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-wrap-function": "^7.1.0", - "@babel/template": "^7.1.0", - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-replace-supers": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.4.4.tgz", - "integrity": "sha512-04xGEnd+s01nY1l15EuMS1rfKktNF+1CkKmHoErDppjAAZL+IUBZpzT748x262HF7fibaQPhbvWUl5HeSt1EXg==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.0.0", - "@babel/helper-optimise-call-expression": "^7.0.0", - "@babel/traverse": "^7.4.4", - "@babel/types": "^7.4.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", - "dev": true - }, - "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "@babel/helper-simple-access": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz", - "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==", - "dev": true, - "requires": { - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", - "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-wrap-function": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz", - "integrity": "sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/template": "^7.1.0", - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.2.0" - }, - "dependencies": { - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - } - } - }, - "@babel/helpers": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.4.tgz", - "integrity": "sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A==", - "dev": true, - "requires": { - "@babel/template": "^7.4.4", - "@babel/traverse": "^7.4.4", - "@babel/types": "^7.4.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", - "dev": true - }, - "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" - } - }, - "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.1.6.tgz", - "integrity": "sha512-dWP6LJm9nKT6ALaa+bnL247GHHMWir3vSlZ2+IHgHgktZQx0L3Uvq2uAWcuzIe+fujRsYWBW2q622C5UvGK9iQ==", - "dev": true - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz", - "integrity": "sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.1.0", - "@babel/plugin-syntax-async-generators": "^7.2.0" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz", - "integrity": "sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-json-strings": "^7.2.0" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz", - "integrity": "sha512-dMBG6cSPBbHeEBdFXeQ2QLc5gUpg4Vkaz8octD4aoW/ISO+jBOcsuxYL7bsb5WSu8RLP6boxrBIALEHgoHtO9g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.2.0" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz", - "integrity": "sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.2.0" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz", - "integrity": "sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.4.4", - "regexpu-core": "^4.5.4" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz", - "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", - "integrity": "sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", - "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz", - "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", - "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.4.tgz", - "integrity": "sha512-YiqW2Li8TXmzgbXw+STsSqPBPFnGviiaSp6CYOq55X8GQ2SGVLrXB6pNid8HkqkZAzOH6knbai3snhP7v0fNwA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.1.0" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz", - "integrity": "sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.4.tgz", - "integrity": "sha512-jkTUyWZcTrwxu5DD4rWz6rDB5Cjdmgz6z7M7RLXOJyCUkFBawssDGcGh8M/0FTSB87avyJI1HsTwUXp9nKA1PA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "lodash": "^4.17.11" - }, - "dependencies": { - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - } - } - }, - "@babel/plugin-transform-classes": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.4.tgz", - "integrity": "sha512-/e44eFLImEGIpL9qPxSRat13I5QNRgBLu2hOQJCF7VLy/otSM/sypV1+XaIw5+502RX/+6YaSAPmldk+nhHDPw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-define-map": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-optimise-call-expression": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.4.4", - "@babel/helper-split-export-declaration": "^7.4.4", - "globals": "^11.1.0" - }, - "dependencies": { - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - } - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz", - "integrity": "sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.4.tgz", - "integrity": "sha512-/aOx+nW0w8eHiEHm+BTERB2oJn5D127iye/SUQl7NjHy0lf+j7h4MKMMSOwdazGq9OxgiNADncE+SRJkCxjZpQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz", - "integrity": "sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.4.4", - "regexpu-core": "^4.5.4" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.2.0.tgz", - "integrity": "sha512-q+yuxW4DsTjNceUiTzK0L+AfQ0zD9rWaTLiUqHA8p0gxx7lu1EylenfzjeIWNkPy6e/0VG/Wjw9uf9LueQwLOw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz", - "integrity": "sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz", - "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz", - "integrity": "sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==", - "dev": true, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-call-delegate": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz", - "integrity": "sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.4.4", - "@babel/traverse": "^7.4.4", - "@babel/types": "^7.4.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/parser": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz", - "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==", - "dev": true - }, - "@babel/traverse": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz", - "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.5", - "@babel/types": "^7.4.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "@babel/helper-define-map": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.4.4.tgz", - "integrity": "sha512-IX3Ln8gLhZpSuqHJSnTNBWGDE9kdkTEWl21A/K7PQ00tseBwbqCHTvNLHSBd9M0R5rER4h5Rsvj9vw0R5SieBg==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/types": "^7.4.4", - "lodash": "^4.17.11" - }, - "dependencies": { - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - } - } - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz", - "integrity": "sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", - "dev": true, - "requires": { - "@babel/helper-call-delegate": "^7.4.4", - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz", - "integrity": "sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz", - "integrity": "sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - } - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0.tgz", - "integrity": "sha512-avo+lm/QmZlv27Zsi0xEor2fKcqWG56D5ae9dzklpIaY7cQMK5N8VSpaNVPPagiqmy7LrEjK1IWdGMOqPu5csg==", - "dev": true, - "requires": { - "regenerator-transform": "^0.13.4" - } - }, - "@babel/helper-module-imports": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", - "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, "@babel/helper-module-transforms": { "version": "7.4.4", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.4.4.tgz", diff --git a/src/common/createJob.js b/src/common/createJob.js new file mode 100644 index 0000000..a7e3b48 --- /dev/null +++ b/src/common/createJob.js @@ -0,0 +1,27 @@ +const { sendPacket } = require('../node'); + +let jobCounter = 0; + +module.exports = ( + action, + payload, +) => { + jobCounter += 1; + const id = `Job-${jobCounter}-${Math.random().toString(16).slice(3, 8)}`; + + const start = (worker) => { + console.log(`[${worker.id}]: Start ${id}, action=${action}`); + sendPacket(worker, { + workerId: worker.id, + jobId: id, + action, + payload, + }); + }; + + return { + id, + action, + start, + }; +}; diff --git a/src/common/createScheduler.js b/src/common/createScheduler.js new file mode 100644 index 0000000..23b3435 --- /dev/null +++ b/src/common/createScheduler.js @@ -0,0 +1,57 @@ +module.exports = () => { + const workers = {}; + const runningJobs = {}; + let jobQueue = []; + + const dequeue = () => { + if (jobQueue.length !== 0) { + const wIds = Object.keys(workers); + for (let i = 0; i < wIds.length; i += 1) { + if (typeof runningJobs[wIds[i]] === 'undefined') { + jobQueue[0](workers[wIds[i]]); + break; + } + } + } + }; + + const queue = job => ( + new Promise((resolve, reject) => { + jobQueue.push((w) => { + const { action } = job; + jobQueue.shift(); + w.setResolve(action, (data) => { + delete runningJobs[w.id]; + dequeue(); + resolve(data); + }); + w.setReject(action, reject); + runningJobs[w.id] = job; + job.start(w); + }); + dequeue(); + }) + ); + + const addWorker = (w) => { + workers[w.id] = w; + return w.id; + }; + + const addJob = job => ( + queue(job) + ); + + const terminate = () => { + Object.keys(workers).forEach((id) => { + workers[id].terminate(); + }); + jobQueue = []; + }; + + return { + addWorker, + addJob, + terminate, + }; +}; diff --git a/src/common/createTesseract.js b/src/common/createTesseract.js new file mode 100644 index 0000000..fe0c1d1 --- /dev/null +++ b/src/common/createTesseract.js @@ -0,0 +1,7 @@ +module.exports = (options = {}, nWorkers = 1) => { + return { + init: () => {}, + loadLanguauge: () => {}, + recognize: () => {}, + }; +}; diff --git a/src/common/createWorker.js b/src/common/createWorker.js new file mode 100644 index 0000000..0c56e02 --- /dev/null +++ b/src/common/createWorker.js @@ -0,0 +1,129 @@ +const { isBrowser } = require('./env'); +const resolveURL = isBrowser ? require('resolve-url') : s => s; // eslint-disable-line +const circularize = require('./circularize'); +const createJob = require('./createJob'); +const { defaultParams } = require('./options'); +const { + defaultOptions, + spawnWorker, + terminateWorker, + setOnMessage, +} = require('../node'); + +let workerCounter = 0; + +const resolvePaths = (options) => { + const opts = { ...options }; + ['corePath', 'workerPath', 'langPath'].forEach((key) => { + if (typeof options[key] !== 'undefined') { + opts[key] = resolveURL(opts[key]); + } + }); + return opts; +}; + +module.exports = (options = {}) => { + workerCounter += 1; + const id = `Worker-${workerCounter}-${Math.random().toString(16).slice(3, 8)}`; + const opts = resolvePaths({ + ...defaultOptions, + ...options, + }); + const { logger } = opts; + const resolves = {}; + const rejects = {}; + let worker = spawnWorker(opts); + + const setResolve = (action, res) => { + resolves[action] = res; + }; + + const setReject = (action, rej) => { + rejects[action] = rej; + }; + + const load = () => ( + new Promise((resolve, reject) => { + const job = createJob( + 'load', + opts, + ); + setResolve('load', resolve); + setReject('load', reject); + job.start({ worker, id }); + }) + ); + + const loadLanguage = (langs = 'eng') => ( + new Promise((resolve, reject) => { + const job = createJob( + 'load-language', + { + langs, + options: opts, + }, + ); + setResolve('load-language', resolve); + setReject('load-language', reject); + job.start({ worker, id }); + }) + ); + + const initialize = (langs = 'eng', params = {}) => ( + new Promise((resolve, reject) => { + const job = createJob( + 'initialize', + { + langs, + params: { + ...defaultParams, + ...params, + }, + }, + ); + setResolve('initialize', resolve); + setReject('initialize', reject); + job.start({ worker, id }); + }) + ); + + const terminate = () => { + if (worker !== null) { + terminateWorker({ worker }); + worker = null; + } + }; + + setOnMessage(worker, (packet) => { + const { status, action, data } = packet; + if (status === 'resolve') { + if (action === 'load') { + resolves.load(data); + } else if (action === 'initialize') { + resolves.initialize({ id }); + } else if (action === 'load-language') { + resolves['load-language'](data); + } else if (action === 'recognize') { + resolves.recognize(circularize(data)); + } else if (action === 'detect') { + resolves.detect(data); + } + } else if (status === 'reject') { + rejects[action](data); + throw Error(data); + } else if (status === 'progress') { + logger(data); + } + }); + + return { + id, + worker, + setResolve, + setReject, + load, + loadLanguage, + initialize, + terminate, + }; +}; diff --git a/src/common/env.js b/src/common/env.js new file mode 100644 index 0000000..f8e49ad --- /dev/null +++ b/src/common/env.js @@ -0,0 +1 @@ +exports.isBrowser = (typeof window !== 'undefined') && (typeof window.document !== 'undefined'); diff --git a/src/common/options.js b/src/common/options.js index a86a263..65e7936 100644 --- a/src/common/options.js +++ b/src/common/options.js @@ -12,6 +12,7 @@ module.exports = { * Use BlobURL for worker script by default */ workerBlobURL: true, + logger: () => {}, }, /* * default params for recognize() diff --git a/src/common/utils.js b/src/common/utils.js new file mode 100644 index 0000000..51edf83 --- /dev/null +++ b/src/common/utils.js @@ -0,0 +1,87 @@ +const { readImage } = require('tesseract.js-utils'); + +/** + * setImage + * + * @name setImage + * @function set image in tesseract for recognition + * @access public + * @param {array} image - binary array in array format + * @returns {number} - an emscripten pointer of the image + */ +exports.setImage = (TessModule, api, image, params) => { + const { + tessjs_image_rectangle_left: left, + tessjs_image_rectangle_top: top, + tessjs_image_rectangle_width: width, + tessjs_image_rectangle_height: height, + } = params; + const { + w, h, bytesPerPixel, data, pix, + } = readImage(TessModule, Array.from(image)); + + /* + * As some image format (ex. bmp) is not supported natiely by tesseract, + * sometimes it will not return pix directly, but data and bytesPerPixel + * for another SetImage usage. + * + */ + if (data === null) { + api.SetImage(pix); + } else { + api.SetImage(data, w, h, bytesPerPixel, w * bytesPerPixel); + } + api.SetRectangle( + (left < 0) ? 0 : left, + (top < 0) ? 0 : top, + (width < 0) ? w : width, + (height < 0) ? h : height, + ); + return data === null ? pix : data; +}; + +exports.getLangsStr = langs => ( + typeof langs === 'string' + ? langs + : langs.map(lang => (typeof lang === 'string' ? lang : lang.data)).join('+') +); + +/** + * handleOutput + * + * @name handleOutput + * @function handle file output + * @access private + * @param {object} customParams - an object of params + */ +exports.getFiles = (TessModule, api, adapter, params) => { + let files = {}; + const { + tessjs_create_pdf, + tessjs_textonly_pdf, + tessjs_pdf_name, + tessjs_pdf_title, + tessjs_pdf_auto_download, + tessjs_pdf_bin, + } = params; + + if (tessjs_create_pdf === '1') { + const pdfRenderer = new TessModule.TessPDFRenderer(tessjs_pdf_name, '/', tessjs_textonly_pdf === '1'); + pdfRenderer.BeginDocument(tessjs_pdf_title); + pdfRenderer.AddImage(api); + pdfRenderer.EndDocument(); + TessModule._free(pdfRenderer); + + const data = TessModule.FS.readFile(`/${tessjs_pdf_name}.pdf`); + + if (tessjs_pdf_bin) { + files = { pdf: data, ...files }; + } + + if (tessjs_pdf_auto_download) { + adapter.writeFile(`${tessjs_pdf_name}.pdf`, data, 'application/pdf'); + } + } + + return files; +}; diff --git a/src/common/workerUtils.js b/src/common/workerUtils.js index 5d582d0..8a12660 100644 --- a/src/common/workerUtils.js +++ b/src/common/workerUtils.js @@ -7,13 +7,12 @@ * @author Guillermo Webster * @author Jerome Wu */ -const { readImage, loadLang } = require('tesseract.js-utils'); +const { loadLang } = require('tesseract.js-utils'); const pdfTTF = require('./pdf-ttf'); const dump = require('./dump'); -const { defaultParams } = require('./options'); const { OEM, PSM } = require('./types'); - -const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined'; +const { isBrowser } = require('./env'); +const { setImage, getLangsStr, getFiles } = require('./utils'); /* * Tesseract Module returned by TesseractCore. @@ -25,117 +24,8 @@ let TessModule; let api; let latestJob; let adapter = {}; +let curParams = {}; -/** - * setImage - * - * @name setImage - * @function set image in tesseract for recognition - * @access public - * @param {array} image - binary array in array format - * @returns {number} - an emscripten pointer of the image - */ -const setImage = (image, params) => { - const { - tessjs_image_rectangle_left: left, - tessjs_image_rectangle_top: top, - tessjs_image_rectangle_width: width, - tessjs_image_rectangle_height: height, - } = params; - const { - w, h, bytesPerPixel, data, pix, - } = readImage(TessModule, Array.from(image)); - - /* - * As some image format (ex. bmp) is not supported natiely by tesseract, - * sometimes it will not return pix directly, but data and bytesPerPixel - * for another SetImage usage. - * - */ - if (data === null) { - api.SetImage(pix); - } else { - api.SetImage(data, w, h, bytesPerPixel, w * bytesPerPixel); - } - api.SetRectangle( - (left < 0) ? 0 : left, - (top < 0) ? 0 : top, - (width < 0) ? w : width, - (height < 0) ? h : height, - ); - return data === null ? pix : data; -}; - -const getLangsStr = langs => ( - typeof langs === 'string' - ? langs - : langs.map(lang => (typeof lang === 'string' ? lang : lang.data)).join('+') -); - -/** - * handleParams - * - * @name handleParams - * @function hanlde params from users - * @access private - * @param {string} langs - lang string for Init() - * @param {object} customParams - an object of params - */ -const handleParams = (langs, iParams) => { - const { - tessedit_ocr_engine_mode, - ...params - } = iParams; - api.Init(null, getLangsStr(langs), tessedit_ocr_engine_mode); - Object.keys(params).forEach((key) => { - if (!key.startsWith('tessjs')) { - api.SetVariable(key, params[key]); - } - }); -}; - -/** - * handleOutput - * - * @name handleOutput - * @function handle file output - * @access private - * @param {object} customParams - an object of params - */ -const handleOutput = (customParams) => { - let files = {}; - const { - tessjs_create_pdf, - tessjs_textonly_pdf, - tessjs_pdf_name, - tessjs_pdf_title, - tessjs_pdf_auto_download, - tessjs_pdf_bin, - } = { - ...defaultParams, - ...customParams, - }; - - if (tessjs_create_pdf === '1') { - const pdfRenderer = new TessModule.TessPDFRenderer(tessjs_pdf_name, '/', tessjs_textonly_pdf === '1'); - pdfRenderer.BeginDocument(tessjs_pdf_title); - pdfRenderer.AddImage(api); - pdfRenderer.EndDocument(); - TessModule._free(pdfRenderer); - - const data = TessModule.FS.readFile(`/${tessjs_pdf_name}.pdf`); - - if (tessjs_pdf_bin) { - files = { pdf: data, ...files }; - } - - if (tessjs_pdf_auto_download) { - adapter.writeFile(`${tessjs_pdf_name}.pdf`, data, 'application/pdf'); - } - } - - return files; -}; /** * handleInit @@ -148,26 +38,32 @@ const handleOutput = (customParams) => { * @param {object} res - job instance * @returns {Promise} A Promise for callback */ -const handleInit = ({ corePath }, res) => { +const load = ({ workerId, jobId, payload: { corePath } }, res) => { if (!TessModule) { const Core = adapter.getCore(corePath, res); - res.progress({ status: 'initializing tesseract', progress: 0 }); + res.progress({ workerId, status: 'initializing tesseract', progress: 0 }); - return Core({ + Core({ TesseractProgress(percent) { - latestJob.progress({ status: 'recognizing text', progress: Math.max(0, (percent - 30) / 70) }); + latestJob.progress({ + workerId, + jobId, + status: 'recognizing text', + progress: Math.max(0, (percent - 30) / 70), + }); }, }) .then((tessModule) => { TessModule = tessModule; TessModule.FS.writeFile('/pdf.ttf', adapter.b64toU8Array(pdfTTF)); api = new TessModule.TessBaseAPI(); - res.progress({ status: 'initialized tesseract', progress: 1 }); + res.progress({ workerId, status: 'initialized tesseract', progress: 1 }); + res.resolve({ loaded: true }); }); + } else { + res.resolve({ loaded: true }); } - - return Promise.resolve(); }; /** @@ -182,14 +78,59 @@ const handleInit = ({ corePath }, res) => { * @param {object} res - job instance * @returns {Promise} A Promise for callback */ -const loadLanguage = ({ langs, options }, res) => { - res.progress({ status: 'loading language traineddata', progress: 0 }); - return loadLang({ langs, TessModule, ...options }).then((...args) => { - res.progress({ status: 'loaded language traineddata', progress: 1 }); - return args; +const loadLanguage = ({ workerId, payload: { langs, options } }, res) => { + res.progress({ workerId, status: 'loading language traineddata', progress: 0 }); + loadLang({ langs, TessModule, ...options }).then(() => { + res.progress({ workerId, status: 'loaded language traineddata', progress: 1 }); + res.resolve(langs); + }).catch((e) => { + if (isBrowser && e instanceof DOMException) { + /* + * For some reason google chrome throw DOMException in loadLang, + * while other browser is OK, for now we ignore this exception + * and hopefully to find the root cause one day. + */ + } else { + res.reject(e.toString()); + } }); }; +const initialize = ({ + workerId, + jobId, + payload: { langs, params }, +}, res) => { + let { tessedit_ocr_engine_mode: oem } = params; + let l = langs; + + res.progress({ + workerId, jobId, status: 'initializing api', progress: 0, + }); + if ([ + PSM.OSD_ONLY, + PSM.AUTO_OSD, + PSM.RAW_LINE, + ].includes(params.tessedit_pageseg_mode)) { + l = (typeof l === 'string') ? `${l}+osd` : [...l, 'osd']; + // oem = OEM.TESSERACT_ONLY; + } + api.Init(null, getLangsStr(l), oem); + Object.keys(params).forEach((key) => { + if (!key.startsWith('tessjs')) { + api.SetVariable(key, params[key]); + } + }); + curParams = { + tessedit_ocr_engine_mode: oem, + ...params, + }; + res.progress({ + workerId, jobId, status: 'initialized api', progress: 1, + }); + res.resolve(); +}; + /** * handleRecognize * @@ -203,66 +144,18 @@ const loadLanguage = ({ langs, options }, res) => { * @param {object} req.params - parameters for tesseract * @param {object} res - job instance */ -const handleRecognize = ({ - image, langs: iLangs, options, params: customParams, -}, res) => { - const params = { - ...defaultParams, - ...customParams, - }; - const { tessedit_pageseg_mode } = params; - let langs = iLangs; - - /* - * When PSM === OSD_ONLY or AUTO_OSD or RAW_LINE - * osd.traineddata must be included and - * OEM must be TESSERACT_ONLY (LSTM doesn't support OSD) - */ - if ([ - PSM.OSD_ONLY, - PSM.AUTO_OSD, - PSM.RAW_LINE, - ].includes(tessedit_pageseg_mode)) { - langs = (typeof iLangs === 'string') - ? `${iLangs}+osd` - : [...iLangs, 'osd']; - params.tessedit_ocr_engine_mode = OEM.TESSERACT_ONLY; +const recognize = ({ payload: { image } }, res) => { + try { + const ptr = setImage(TessModule, api, image, curParams); + api.Recognize(null); + res.resolve({ + files: getFiles(TessModule, api, adapter, curParams), + ...dump(TessModule, api, curParams), + }); + TessModule._free(ptr); + } catch (err) { + res.reject(err.toString()); } - return handleInit(options, res) - .then(() => ( - loadLanguage({ langs, params, options }, res) - .catch((e) => { - if (isBrowser && e instanceof DOMException) { - /* - * For some reason google chrome throw DOMException in loadLang, - * while other browser is OK, for now we ignore this exception - * and hopefully to find the root cause one day. - */ - } else { - throw e; - } - }) - .then(() => { - try { - const progressUpdate = (progress) => { - res.progress({ status: 'initializing api', progress }); - }; - progressUpdate(0); - handleParams(langs, params); - progressUpdate(0.5); - const ptr = setImage(image, params); - progressUpdate(1); - api.Recognize(null); - const files = handleOutput(params); - const result = dump(TessModule, api, params); - api.End(); - TessModule._free(ptr); - res.resolve({ files, ...result }); - } catch (err) { - res.reject({ err }); - } - }) - )); }; /** @@ -277,46 +170,34 @@ const handleRecognize = ({ * @param {object} req.options - other options for loadLang function * @param {object} res - job instance */ -const handleDetect = ({ - image, langs, options, params: customParams, -}, res) => ( - handleInit(options, res) - .then(() => ( - loadLanguage({ langs, options }, res) - .then(() => { - api.Init(null, getLangsStr(langs), OEM.TESSERACT_ONLY); - api.SetPageSegMode(PSM.OSD_ONLY); - const params = { - ...defaultParams, - ...customParams, - }; - - const ptr = setImage(image, params); - const results = new TessModule.OSResults(); +const detect = ({ payload: { image } }, res) => { + try { + const ptr = setImage(TessModule, api, image, curParams); + const results = new TessModule.OSResults(); - if (!api.DetectOS(results)) { - api.End(); - TessModule._free(ptr); - res.reject('Failed to detect OS'); - } else { - const best = results.best_result; - const oid = best.orientation_id; - const sid = best.script_id; + if (!api.DetectOS(results)) { + api.End(); + TessModule._free(ptr); + res.reject('Failed to detect OS'); + } else { + const best = results.best_result; + const oid = best.orientation_id; + const sid = best.script_id; - api.End(); - TessModule._free(ptr); + TessModule._free(ptr); - res.resolve({ - tesseract_script_id: sid, - script: results.unicharset.get_script_from_script_id(sid), - script_confidence: best.sconfidence, - orientation_degrees: [0, 270, 180, 90][oid], - orientation_confidence: best.oconfidence, - }); - } - }) - )) -); + res.resolve({ + tesseract_script_id: sid, + script: results.unicharset.get_script_from_script_id(sid), + script_confidence: best.sconfidence, + orientation_degrees: [0, 270, 180, 90][oid], + orientation_confidence: best.oconfidence, + }); + } + } catch (err) { + res.reject(err.toString()); + } +}; /** * dispatchHandlers @@ -330,12 +211,11 @@ const handleDetect = ({ * @param {object} data.payload - data for the job * @param {function} send - trigger job to work */ -exports.dispatchHandlers = ({ jobId, action, payload }, send) => { +exports.dispatchHandlers = (packet, send) => { const res = (status, data) => { send({ - jobId, + ...packet, status, - action, data, }); }; @@ -346,10 +226,17 @@ exports.dispatchHandlers = ({ jobId, action, payload }, send) => { latestJob = res; try { - if (action === 'recognize') { - handleRecognize(payload, res); + const { action } = packet; + if (action === 'load') { + load(packet, res); + } else if (action === 'load-language') { + loadLanguage(packet, res); + } else if (action === 'initialize') { + initialize(packet, res); + } else if (action === 'recognize') { + recognize(packet, res); } else if (action === 'detect') { - handleDetect(payload, res); + detect(packet, res); } } catch (err) { /** Prepare exception to travel through postMessage */ diff --git a/src/index.js b/src/index.js index 5dc211b..ffda1db 100644 --- a/src/index.js +++ b/src/index.js @@ -9,6 +9,9 @@ */ const utils = require('tesseract.js-utils'); const TesseractWorker = require('./common/TesseractWorker'); +const createScheduler = require('./common/createScheduler'); +const createWorker = require('./common/createWorker'); +const createJob = require('./common/createJob'); const types = require('./common/types'); module.exports = { @@ -18,4 +21,7 @@ module.exports = { utils, /** Check ./common/types for more details */ ...types, + createScheduler, + createWorker, + createJob, }; diff --git a/src/node/index.js b/src/node/index.js index 54d87a1..8103691 100644 --- a/src/node/index.js +++ b/src/node/index.js @@ -67,12 +67,12 @@ exports.defaultOptions = { * @param {object} options * @param {string} options.workerPath - worker script path */ -exports.spawnWorker = (instance, { workerPath }) => { - const cp = fork(workerPath); - cp.on('message', (packet) => { - instance.recv(packet); - }); - return cp; +exports.spawnWorker = ({ workerPath }) => ( + fork(workerPath) +); + +exports.setOnMessage = (worker, handler) => { + worker.on('message', handler); }; /** @@ -83,8 +83,8 @@ exports.spawnWorker = (instance, { workerPath }) => { * @access public * @param {object} instance TesseractWorker instance */ -exports.terminateWorker = (instance) => { - instance.worker.kill(); +exports.terminateWorker = ({ worker }) => { + worker.kill(); }; /** @@ -96,12 +96,16 @@ exports.terminateWorker = (instance) => { * @param {object} instance TesseractWorker instance * @param {object} iPacket data for worker */ -exports.sendPacket = (instance, iPacket) => { - const packet = { ...iPacket }; - loadImage(packet.payload.image) - .then(buf => new Uint8Array(buf)) - .then((img) => { - packet.payload.image = Array.from(img); - instance.worker.send(packet); - }); +exports.sendPacket = ({ worker }, packet) => { + const p = { ...packet }; + if (['recognize', 'detect'].includes(p.action)) { + loadImage(p.payload.image) + .then(buf => new Uint8Array(buf)) + .then((img) => { + p.payload.image = Array.from(img); + worker.send(p); + }); + } else { + worker.send(p); + } };