diff --git a/CliClient/.gitignore b/CliClient/.gitignore index fb2f3fd42..8da9314b8 100644 --- a/CliClient/.gitignore +++ b/CliClient/.gitignore @@ -13,6 +13,7 @@ tests/fuzzing.* tests/fuzzing -* tests/logs/* tests/cli-integration/ +tests/tmp/ *.mo *.*~ tests/sync diff --git a/CliClient/package-lock.json b/CliClient/package-lock.json index c3ab8c311..e9dd15e69 100644 --- a/CliClient/package-lock.json +++ b/CliClient/package-lock.json @@ -38,6 +38,14 @@ "json-schema-traverse": "^0.3.0" } }, + "ansi-escape-sequences": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.0.0.tgz", + "integrity": "sha512-v+0wW9Wezwsyb0uF4aBVCjmSqit3Ru7PZFziGF0o2KwTvN2zWfTi3BRLq9EkJFdg3eBbyERXGTntVpBxH1J68Q==", + "requires": { + "array-back": "^2.0.0" + } + }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -85,6 +93,14 @@ } } }, + "array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "requires": { + "typical": "^2.6.1" + } + }, "array-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", @@ -165,6 +181,44 @@ "tweetnacl": "^0.14.3" } }, + "bl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "boom": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", @@ -187,6 +241,25 @@ "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz", "integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44=" }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" + }, "camel-case": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", @@ -206,17 +279,6 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, - "caw": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz", - "integrity": "sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==", - "requires": { - "get-proxy": "^2.0.0", - "isurl": "^1.0.0-alpha5", - "tunnel-agent": "^0.6.0", - "url-to-options": "^1.0.1" - } - }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", @@ -245,6 +307,21 @@ "source-map": "0.5.x" } }, + "cliss": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/cliss/-/cliss-0.0.2.tgz", + "integrity": "sha512-6rj9pgdukjT994Md13JCUAgTk91abAKrygL9sAvmHY4F6AKMOV8ccGaxhUUfcBuyg3sundWnn3JE0Mc9W6ZYqw==", + "requires": { + "command-line-usage": "^4.0.1", + "deepmerge": "^2.0.0", + "get-stdin": "^5.0.1", + "inspect-parameters-declaration": "0.0.9", + "object-to-arguments": "0.0.8", + "pipe-functions": "^1.3.0", + "strip-ansi": "^4.0.0", + "yargs-parser": "^7.0.0" + } + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -256,12 +333,22 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-2.0.0.tgz", - "integrity": "sha1-4MmXLR6WmFcASxAeqlXOq1lh1n0=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", "requires": { - "color-convert": "^1.8.2", - "color-string": "^1.4.0" + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + }, + "dependencies": { + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + } } }, "color-convert": { @@ -278,9 +365,9 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-string": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.2.tgz", - "integrity": "sha1-JuRYFLw8mny9Z1FkikFDRRSnc6k=", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", "requires": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" @@ -294,6 +381,17 @@ "delayed-stream": "~1.0.0" } }, + "command-line-usage": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-4.1.0.tgz", + "integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==", + "requires": { + "ansi-escape-sequences": "^4.0.0", + "array-back": "^2.0.0", + "table-layout": "^0.4.2", + "typical": "^2.6.1" + } + }, "commander": { "version": "2.15.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", @@ -309,15 +407,6 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, - "config-chain": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.11.tgz", - "integrity": "sha1-q6CXR9++TD5w52am5BWG4YWfxvI=", - "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -421,6 +510,11 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.2.tgz", "integrity": "sha1-nO1l6gvAsJ9CptecGxkD+dkTzBg=" }, + "deepmerge": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.1.1.tgz", + "integrity": "sha512-urQxA1smbLZ2cBbXbaYObM1dJ82aJ2H57A1C/Kklfh/ZN1bgH4G/n5KWhdNfOK11W98gqZfyYj7W4frJJRwA2w==" + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -432,9 +526,9 @@ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, "detect-libc": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-0.2.0.tgz", - "integrity": "sha1-R/31ZzSKF+wl/L8LnkRjSKdvn7U=" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" }, "domexception": { "version": "1.0.1", @@ -508,6 +602,14 @@ "iconv-lite": "~0.4.13" } }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "^1.4.0" + } + }, "entities": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", @@ -564,6 +666,11 @@ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", "dev": true }, + "expand-template": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.1.tgz", + "integrity": "sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg==" + }, "extend": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", @@ -602,6 +709,14 @@ "resolved": "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz", "integrity": "sha1-G2AOX8ofvcboDApwxxyNul95BsU=" }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, "follow-redirects": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.2.5.tgz", @@ -610,6 +725,22 @@ "debug": "^2.6.9" } }, + "for-each-property": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/for-each-property/-/for-each-property-0.0.4.tgz", + "integrity": "sha1-z6hXrsFCLh0Sb/CHhPz2Jim8g/Y=", + "requires": { + "get-prototype-chain": "^1.0.1" + } + }, + "for-each-property-deep": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/for-each-property-deep/-/for-each-property-deep-0.0.3.tgz", + "integrity": "sha1-MTCaSvw4qcygbxsiP1PWSm0IP60=", + "requires": { + "for-each-property": "0.0.4" + } + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -630,6 +761,16 @@ "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", "integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=" }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-copy-file-sync": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fs-copy-file-sync/-/fs-copy-file-sync-1.1.1.tgz", + "integrity": "sha512-2QY5eeqVv4m2PfyMiEuy9adxNP+ajf+8AR05cEi+OAzPcOj90hvFImeZhTmKLBgSd9EvG33jsD7ZRxsx9dThkQ==" + }, "fs-extra": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", @@ -719,13 +860,15 @@ "through": "^2.3.4" } }, - "get-proxy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", - "integrity": "sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw==", - "requires": { - "npm-conf": "^1.1.0" - } + "get-prototype-chain": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-prototype-chain/-/get-prototype-chain-1.0.1.tgz", + "integrity": "sha1-oXGhFeoeSQbG7ThDofABwYUQQW8=" + }, + "get-stdin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", + "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=" }, "getpass": { "version": "0.1.7", @@ -735,6 +878,11 @@ "assert-plus": "^1.0.0" } }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -787,19 +935,6 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, - "has-symbol-support-x": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.1.tgz", - "integrity": "sha512-JkaetveU7hFbqnAC1EV1sF4rlojU2D4Usc5CmS69l6NfmPDnpnFUegzFg33eDkkpNCxZ0mQp65HwUDrNFS/8MA==" - }, - "has-to-string-tag-x": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", - "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", - "requires": { - "has-symbol-support-x": "^1.4.1" - } - }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -881,6 +1016,150 @@ "minimatch": "^3.0.4" } }, + "image-data-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/image-data-uri/-/image-data-uri-2.0.0.tgz", + "integrity": "sha512-PhIJxgfSQai/Xy8Nij1lWgK6++Y6x/ga2FKQTd8F71Nz2ArqtFr1F1UAREK0twrfp7mcEqidgGSF06No14/m+Q==", + "requires": { + "fs-extra": "^0.26.7", + "magicli": "0.0.8", + "mime-types": "^2.1.18", + "request": "^2.88.0" + }, + "dependencies": { + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + }, + "dependencies": { + "combined-stream": { + "version": "1.0.6", + "resolved": "http://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "~1.0.0" + } + } + } + }, + "fs-extra": { + "version": "0.26.7", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz", + "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "har-validator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", + "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", + "requires": { + "ajv": "^5.3.0", + "har-schema": "^2.0.0" + } + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "mime-db": { + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", + "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==" + }, + "mime-types": { + "version": "2.1.20", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz", + "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==", + "requires": { + "mime-db": "~1.36.0" + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, "image-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/image-type/-/image-type-3.0.0.tgz", @@ -908,6 +1187,120 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=" }, + "inspect-function": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.2.2.tgz", + "integrity": "sha1-hdoMUli8TDMK4yg7Z0fgdZ2QpjU=", + "requires": { + "split-skip": "0.0.1", + "unpack-string": "0.0.2" + }, + "dependencies": { + "split-skip": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.1.tgz", + "integrity": "sha1-gK2ONumOV2RUzDtmfB3SXYZejwA=" + } + } + }, + "inspect-parameters-declaration": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/inspect-parameters-declaration/-/inspect-parameters-declaration-0.0.9.tgz", + "integrity": "sha512-c3jrKKA1rwwrsjdGMAo2hFWV0vNe3/RKHxpE/OBt41LP3ynOVI1qmgxpZYK5SQu3jtWCyaho8L7AZzCjJ4mEUw==", + "requires": { + "magicli": "0.0.5", + "split-skip": "0.0.2", + "stringify-parameters": "0.0.4", + "unpack-string": "0.0.2" + }, + "dependencies": { + "magicli": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz", + "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=", + "requires": { + "commander": "^2.9.0", + "get-stdin": "^5.0.1", + "inspect-function": "^0.2.1", + "pipe-functions": "^1.2.0" + } + } + } + }, + "inspect-property": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/inspect-property/-/inspect-property-0.0.6.tgz", + "integrity": "sha512-LgjHkRl9W6bj2n+kWrAOgvCYPTYt+LanE4rtd/vKNq6yEb+SvVV7UTLzoSPpDX6/U1cAz7VfqPr+lPAIz7wHaQ==", + "requires": { + "for-each-property": "0.0.4", + "for-each-property-deep": "0.0.3", + "inspect-function": "^0.3.1" + }, + "dependencies": { + "inspect-function": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.3.4.tgz", + "integrity": "sha512-s0RsbJqK/sNZ+U1mykGoTickog3ea1A9Qk4mXniogOBu4PgkkZ56elScO7QC/r8D94lhGmJ2NyDI1ipOA/uq/g==", + "requires": { + "inspect-parameters-declaration": "0.0.8", + "magicli": "0.0.8", + "split-skip": "0.0.1", + "stringify-parameters": "0.0.4", + "unpack-string": "0.0.2" + } + }, + "inspect-parameters-declaration": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/inspect-parameters-declaration/-/inspect-parameters-declaration-0.0.8.tgz", + "integrity": "sha512-W4QzN1LgFmasKOM+NoLlDd2OAZM3enNZlVUOXoGQKmYBDFgxoPDOyebF55ALaf8avyM9TavNwibXxg347RrzCg==", + "requires": { + "magicli": "0.0.5", + "split-skip": "0.0.2", + "stringify-parameters": "0.0.4", + "unpack-string": "0.0.2" + }, + "dependencies": { + "inspect-function": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.2.2.tgz", + "integrity": "sha1-hdoMUli8TDMK4yg7Z0fgdZ2QpjU=", + "requires": { + "split-skip": "0.0.1", + "unpack-string": "0.0.2" + }, + "dependencies": { + "split-skip": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.1.tgz", + "integrity": "sha1-gK2ONumOV2RUzDtmfB3SXYZejwA=" + } + } + }, + "magicli": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz", + "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=", + "requires": { + "commander": "^2.9.0", + "get-stdin": "^5.0.1", + "inspect-function": "^0.2.1", + "pipe-functions": "^1.2.0" + } + }, + "split-skip": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.2.tgz", + "integrity": "sha1-2J2Iu9L3Pka1FYqjcKVhIk6A1GE=" + } + } + }, + "split-skip": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.1.tgz", + "integrity": "sha1-gK2ONumOV2RUzDtmfB3SXYZejwA=" + } + } + }, "iota-array": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", @@ -923,9 +1316,9 @@ } }, "is-arrayish": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.1.tgz", - "integrity": "sha1-wt/DhquqDD4zxI2z/ocFnmkGXv0=" + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, "is-buffer": { "version": "1.1.5", @@ -937,11 +1330,6 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, - "is-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", - "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" - }, "is-relative": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz", @@ -991,15 +1379,6 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, - "isurl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", - "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", - "requires": { - "has-to-string-tag-x": "^1.2.0", - "is-object": "^1.0.1" - } - }, "jasmine": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", @@ -1118,6 +1497,14 @@ "resolved": "https://registry.npmjs.org/jssha/-/jssha-2.3.1.tgz", "integrity": "sha1-FHshJTaQNcpLL30hDcU58Amz3po=" }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "requires": { + "graceful-fs": "^4.1.9" + } + }, "left-pad": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", @@ -1145,6 +1532,15 @@ "uc.micro": "^1.0.1" } }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", @@ -1155,6 +1551,11 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.4.tgz", "integrity": "sha1-3MHXVS4VCgZABzupyzHXDwMpUOc=" }, + "lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=" + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -1187,6 +1588,17 @@ "highlight.js": "~9.12.0" } }, + "magicli": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.8.tgz", + "integrity": "sha512-x/eBenweAHF+DsYy172sK4doRxZl0yrJnfxhLJiN7H6hPM3Ya0PfI6uBZshZ3ScFFSQD7HXgBqMdbnXKEZsO1g==", + "requires": { + "cliss": "0.0.2", + "find-up": "^2.1.0", + "for-each-property": "0.0.4", + "inspect-property": "0.0.6" + } + }, "markdown-it": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", @@ -1233,9 +1645,9 @@ } }, "mimic-response": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.0.tgz", - "integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" }, "minimatch": { "version": "3.0.4", @@ -1285,9 +1697,9 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "nan": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", - "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=" + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz", + "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==" }, "ndarray": { "version": "1.0.18", @@ -1330,6 +1742,14 @@ "lower-case": "^1.1.1" } }, + "node-abi": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.4.4.tgz", + "integrity": "sha512-DQ9Mo2mf/XectC+s6+grPPRQ1Z9gI3ZbrGv6nyXRkjwT3HrE0xvtvrfnH7YHYBLgC/KLadg+h3XHnhZw1sv88A==", + "requires": { + "semver": "^5.4.1" + } + }, "node-bitmap": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/node-bitmap/-/node-bitmap-0.0.1.tgz", @@ -1386,6 +1806,11 @@ } } }, + "noop-logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", + "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=" + }, "nopt": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", @@ -1400,15 +1825,6 @@ "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.3.tgz", "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==" }, - "npm-conf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.2.tgz", - "integrity": "sha512-dotwbpwVzfNB/2EF3A2wjK5tEMLggKfuA/8TG6WvBB1Zrv+JsvF7E8ei9B/HGq211st/GwXFbREcNJvJ1eySUQ==", - "requires": { - "config-chain": "^1.1.11", - "pify": "^3.0.0" - } - }, "npm-packlist": { "version": "1.1.10", "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.10.tgz", @@ -1449,6 +1865,42 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "object-to-arguments": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/object-to-arguments/-/object-to-arguments-0.0.8.tgz", + "integrity": "sha512-BfWfuAwuhdH1bhMG5EG90WE/eckkBhBvnke8eSEkCDXoLE9Jk5JwYGTbCx1ehGwV48HvBkn62VukPBdlMUOY9w==", + "requires": { + "inspect-parameters-declaration": "0.0.10", + "magicli": "0.0.5", + "split-skip": "0.0.2", + "stringify-parameters": "0.0.4", + "unpack-string": "0.0.2" + }, + "dependencies": { + "inspect-parameters-declaration": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/inspect-parameters-declaration/-/inspect-parameters-declaration-0.0.10.tgz", + "integrity": "sha512-L8/Bvt9iDXQTZ63xY5/MAyvzz+FagR/qGh1kIXvUpsno3AAE0Z95d6QO51zrcMGaEGpwh/57idfMxTxbvRmytg==", + "requires": { + "magicli": "0.0.5", + "split-skip": "0.0.2", + "stringify-parameters": "0.0.4", + "unpack-string": "0.0.2" + } + }, + "magicli": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz", + "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=", + "requires": { + "commander": "^2.9.0", + "get-stdin": "^5.0.1", + "inspect-function": "^0.2.1", + "pipe-functions": "^1.2.0" + } + } + } + }, "omggif": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.9.tgz", @@ -1501,6 +1953,27 @@ "os-tmpdir": "^1.0.0" } }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + }, "param-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", @@ -1522,6 +1995,11 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==" }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1537,6 +2015,11 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" }, + "pipe-functions": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pipe-functions/-/pipe-functions-1.3.0.tgz", + "integrity": "sha512-6Rtbp7criZRwedlvWbUYxqlqJoAlMvYHo2UcRWq79xZ54vZcaNHpVBOcWkX3ErT2aUA69tv+uiv4zKJbhD/Wgg==" + }, "pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", @@ -1547,6 +2030,35 @@ "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-2.3.1.tgz", "integrity": "sha1-EdHhK5y2TWPjDBQ6Mw9MH1Z9qF8=" }, + "prebuild-install": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-4.0.0.tgz", + "integrity": "sha512-7tayxeYboJX0RbVzdnKyGl2vhQRWr6qfClEXDhOkXjuaOKCw2q8aiuFhONRYVsG/czia7KhpykIlI2S2VaPunA==", + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^1.0.2", + "github-from-package": "0.0.0", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "node-abi": "^2.2.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "os-homedir": "^1.0.1", + "pump": "^2.0.1", + "rc": "^1.1.6", + "simple-get": "^2.7.0", + "tar-fs": "^1.13.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -1574,10 +2086,19 @@ "retry": "^0.10.0" } }, - "proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=" + "psl": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", + "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } }, "punycode": { "version": "1.4.1", @@ -1649,6 +2170,11 @@ "util-deprecate": "~1.0.1" } }, + "reduce-flatten": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", + "integrity": "sha1-JYx479FT3fk8tWEjf2EYTzaW4yc=" + }, "redux": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz", @@ -1756,28 +2282,52 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "sharp": { - "version": "0.18.4", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.18.4.tgz", - "integrity": "sha1-/jKcDwaJbCiqJDdt8f/wKuV/LTQ=", + "version": "0.20.8", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.20.8.tgz", + "integrity": "sha512-A8NaPGWRDKpmHTi8sl2xzozYXhTQWBb/GaJ8ZPU7L/vKW8wVvd4Yq+isJ0c7p9sX5gnjPQcM3eOfHuvvnZ2fOQ==", "requires": { - "caw": "^2.0.0", - "color": "^2.0.0", - "detect-libc": "^0.2.0", - "nan": "^2.6.2", - "semver": "^5.3.0", - "simple-get": "^2.7.0", - "tar": "^3.1.5" + "color": "^3.0.0", + "detect-libc": "^1.0.3", + "fs-copy-file-sync": "^1.1.1", + "nan": "^2.11.0", + "npmlog": "^4.1.2", + "prebuild-install": "^4.0.0", + "semver": "^5.5.1", + "simple-get": "^2.8.1", + "tar": "^4.4.6", + "tunnel-agent": "^0.6.0" }, "dependencies": { + "minipass": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.4.tgz", + "integrity": "sha512-mlouk1OHlaUE8Odt1drMtG1bAJA4ZA6B/ehysgV0LUIrDHdKgo1KorZq3pK0b/7Z7LJIQ12MNM6aC+Tn6lUZ5w==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==" + }, "tar": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-3.2.1.tgz", - "integrity": "sha512-ZSzds1E0IqutvMU8HxjMaU8eB7urw2fGwTq88ukDOVuUIh0656l7/P7LiVPxhO5kS4flcRJQk8USG+cghQbTUQ==", + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.6.tgz", + "integrity": "sha512-tMkTnh9EdzxyfW+6GK6fCahagXsnYk6kE6S9Gr9pjVdys769+laCTbodXDhPAjzVtEBazRgP0gYqOjnk9dQzLg==", "requires": { "chownr": "^1.0.1", - "minipass": "^2.0.2", - "minizlib": "^1.0.3", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.3", + "minizlib": "^1.1.0", "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", "yallist": "^3.0.2" } } @@ -1794,9 +2344,9 @@ "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" }, "simple-get": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.7.0.tgz", - "integrity": "sha512-RkE9rGPHcxYZ/baYmgJtOSM63vH0Vyq+ma5TijBcLla41SWlh8t6XYIGMR/oeZcmr+/G8k+zrClkkVrtnQ0esg==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", "requires": { "decompress-response": "^3.3.0", "once": "^1.3.1", @@ -1832,6 +2382,11 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, + "split-skip": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.2.tgz", + "integrity": "sha1-2J2Iu9L3Pka1FYqjcKVhIk6A1GE=" + }, "sprintf-js": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz", @@ -1917,6 +2472,28 @@ "safe-buffer": "~5.1.0" } }, + "stringify-parameters": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stringify-parameters/-/stringify-parameters-0.0.4.tgz", + "integrity": "sha512-H3L90ERn5UPtkpO8eugnKcLgpIVlvTyUTrcLGm607AV5JDH6z0GymtNLr3gjGlP6I6NB/mxNX9QpY6jEQGLPdQ==", + "requires": { + "magicli": "0.0.5", + "unpack-string": "0.0.2" + }, + "dependencies": { + "magicli": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz", + "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=", + "requires": { + "commander": "^2.9.0", + "get-stdin": "^5.0.1", + "inspect-function": "^0.2.1", + "pipe-functions": "^1.2.0" + } + } + } + }, "stringstream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", @@ -1958,6 +2535,18 @@ "resolved": "https://registry.npmjs.org/syswide-cas/-/syswide-cas-5.2.0.tgz", "integrity": "sha512-I81xC4l8ECPonkPyYPYUw+IO1ebE4W3fMV9KeavvF/5Snlg3T1DLhWFTm9C32YVOHU70VdONPlGCotaBMG2cFA==" }, + "table-layout": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.4.tgz", + "integrity": "sha512-uNaR3SRMJwfdp9OUr36eyEi6LLsbcTqTO/hfTsNviKsNeyMBPICJCC7QXRF3+07bAP6FRwA8rczJPBqXDc0CkQ==", + "requires": { + "array-back": "^2.0.0", + "deep-extend": "~0.6.0", + "lodash.padend": "^4.6.1", + "typical": "^2.6.1", + "wordwrapjs": "^3.0.0" + } + }, "tar": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.0.tgz", @@ -1971,6 +2560,42 @@ "yallist": "^3.0.2" } }, + "tar-fs": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz", + "integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==", + "requires": { + "chownr": "^1.0.1", + "mkdirp": "^0.5.1", + "pump": "^1.0.0", + "tar-stream": "^1.1.2" + }, + "dependencies": { + "pump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + } + }, "tcp-port-used": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-0.1.2.tgz", @@ -2034,6 +2659,11 @@ } } }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" + }, "tough-cookie": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", @@ -2084,6 +2714,11 @@ "prelude-ls": "~1.1.2" } }, + "typical": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", + "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=" + }, "uc.micro": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.5.tgz", @@ -2120,6 +2755,11 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=" }, + "unpack-string": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/unpack-string/-/unpack-string-0.0.2.tgz", + "integrity": "sha1-MC7PCCOLATm9Q0pNf9Z83zPKJ10=" + }, "upper-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", @@ -2134,11 +2774,6 @@ "requires-port": "~1.0.0" } }, - "url-to-options": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", - "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -2200,6 +2835,11 @@ "webidl-conversions": "^4.0.2" } }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=" + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -2218,6 +2858,15 @@ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" }, + "wordwrapjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", + "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", + "requires": { + "reduce-flatten": "^1.0.1", + "typical": "^2.6.1" + } + }, "wrap-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", @@ -2265,6 +2914,11 @@ "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-3.2.0.tgz", "integrity": "sha1-yzYBmHv+JpW1hAAMGPHEqMMih44=" }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, "yallist": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", diff --git a/CliClient/package.json b/CliClient/package.json index 20466d1a8..2af3adfef 100644 --- a/CliClient/package.json +++ b/CliClient/package.json @@ -37,6 +37,7 @@ "fs-extra": "^5.0.0", "html-entities": "^1.2.1", "html-minifier": "^3.5.15", + "image-data-uri": "^2.0.0", "image-type": "^3.0.0", "joplin-turndown": "^4.0.8", "joplin-turndown-plugin-gfm": "^1.0.7", @@ -57,7 +58,7 @@ "redux": "^3.7.2", "sax": "^1.2.2", "server-destroy": "^1.0.1", - "sharp": "^0.18.4", + "sharp": "^0.20.8", "sprintf-js": "^1.1.1", "sqlite3": "^4.0.1", "string-padding": "^1.0.2", diff --git a/CliClient/tests/services_rest_Api.js b/CliClient/tests/services_rest_Api.js new file mode 100644 index 000000000..467657afb --- /dev/null +++ b/CliClient/tests/services_rest_Api.js @@ -0,0 +1,118 @@ +require('app-module-path').addPath(__dirname); + +const { time } = require('lib/time-utils.js'); +const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js'); +const markdownUtils = require('lib/markdownUtils.js'); +const Api = require('lib/services/rest/Api'); +const Folder = require('lib/models/Folder'); +const Resource = require('lib/models/Resource'); + +process.on('unhandledRejection', (reason, p) => { + console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); +}); + +let api = null; + +describe('services_rest_Api', function() { + + beforeEach(async (done) => { + api = new Api(); + await setupDatabaseAndSynchronizer(1); + await switchClient(1); + done(); + }); + + it('should ping', async (done) => { + const response = await api.route('GET', 'ping'); + expect(response).toBe('JoplinClipperServer'); + done(); + }); + + it('should handle Not Found errors', async (done) => { + const hasThrown = await checkThrowAsync(async () => await api.route('GET', 'pong')); + expect(hasThrown).toBe(true); + done(); + }); + + it('should get folders', async (done) => { + let f1 = await Folder.save({ title: "mon carnet" }); + const response = await api.route('GET', 'folders'); + expect(response.length).toBe(1); + expect(response[0].title).toBe('mon carnet'); + done(); + }); + + it('should create notes', async (done) => { + let response = null; + const f = await Folder.save({ title: "mon carnet" }); + + response = await api.route('POST', 'notes', null, JSON.stringify({ + title: 'testing', + parent_id: f.id, + })); + expect(response.title).toBe('testing'); + expect(!!response.id).toBe(true); + + response = await api.route('POST', 'notes', null, JSON.stringify({ + title: 'testing', + parent_id: f.id, + })); + expect(response.title).toBe('testing'); + expect(!!response.id).toBe(true); + + done(); + }); + + it('should create notes with images', async (done) => { + let response = null; + const f = await Folder.save({ title: "mon carnet" }); + + response = await api.route('POST', 'notes', null, JSON.stringify({ + title: 'testing image', + parent_id: f.id, + image_data_url: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANZJREFUeNoAyAA3/wFwtO3K6gUB/vz2+Prw9fj/+/r+/wBZKAAExOgF4/MC9ff+MRH6Ui4E+/0Bqc/zutj6AgT+/Pz7+vv7++nu82c4DlMqCvLs8goA/gL8/fz09fb59vXa6vzZ6vjT5fbn6voD/fwC8vX4UiT9Zi//APHyAP8ACgUBAPv5APz7BPj2+DIaC2o3E+3o6ywaC5fT6gD6/QD9/QEVf9kD+/dcLQgJA/7v8vqfwOf18wA1IAIEVycAyt//v9XvAPv7APz8LhoIAPz9Ri4OAgwARgx4W/6fVeEAAAAASUVORK5CYII=" + })); + + const resources = await Resource.all(); + expect(resources.length).toBe(1); + + const resource = resources[0]; + expect(response.body.indexOf(resource.id) >= 0).toBe(true); + + done(); + }); + + it('should create notes from HTML', async (done) => { + let response = null; + const f = await Folder.save({ title: "mon carnet" }); + + response = await api.route('POST', 'notes', null, JSON.stringify({ + title: 'testing HTML', + parent_id: f.id, + body_html: 'Bold text', + })); + + expect(response.body).toBe('**Bold text**'); + + done(); + }); + + it('should filter fields', async (done) => { + let f = api.fields_({ query: { fields: 'one,two' } }, []); + expect(f.length).toBe(2); + expect(f[0]).toBe('one'); + expect(f[1]).toBe('two'); + + f = api.fields_({ query: { fields: 'one ,, two ' } }, []); + expect(f.length).toBe(2); + expect(f[0]).toBe('one'); + expect(f[1]).toBe('two'); + + f = api.fields_({ query: { fields: ' ' } }, ['def']); + expect(f.length).toBe(1); + expect(f[0]).toBe('def'); + + done(); + }); + +}); \ No newline at end of file diff --git a/CliClient/tests/test-utils.js b/CliClient/tests/test-utils.js index 394f83cc5..c23569800 100644 --- a/CliClient/tests/test-utils.js +++ b/CliClient/tests/test-utils.js @@ -48,7 +48,9 @@ EncryptionService.fsDriver_ = fsDriver; FileApiDriverLocal.fsDriver_ = fsDriver; const logDir = __dirname + '/../tests/logs'; +const tempDir = __dirname + '/../tests/tmp'; fs.mkdirpSync(logDir, 0o755); +fs.mkdirpSync(tempDir, 0o755); SyncTargetRegistry.addClass(SyncTargetMemory); SyncTargetRegistry.addClass(SyncTargetFilesystem); @@ -80,6 +82,7 @@ BaseItem.loadClass('MasterKey', MasterKey); Setting.setConstant('appId', 'net.cozic.joplin-cli'); Setting.setConstant('appType', 'cli'); +Setting.setConstant('tempDir', tempDir); BaseService.logger_ = logger; diff --git a/ReactNativeClient/lib/ClipperServer.js b/ReactNativeClient/lib/ClipperServer.js index f0119d73a..c79ada030 100644 --- a/ReactNativeClient/lib/ClipperServer.js +++ b/ReactNativeClient/lib/ClipperServer.js @@ -1,19 +1,10 @@ const { netUtils } = require('lib/net-utils'); const urlParser = require("url"); -const Note = require('lib/models/Note'); -const Folder = require('lib/models/Folder'); -const Resource = require('lib/models/Resource'); -const Tag = require('lib/models/Tag'); const Setting = require('lib/models/Setting'); -const { shim } = require('lib/shim'); -const md5 = require('md5'); -const { fileExtension, safeFileExtension, safeFilename, filename } = require('lib/path-utils'); -const HtmlToMd = require('lib/HtmlToMd'); const { Logger } = require('lib/logger.js'); -const markdownUtils = require('lib/markdownUtils'); -const mimeUtils = require('lib/mime-utils.js').mime; const randomClipperPort = require('lib/randomClipperPort'); const enableServerDestroy = require('server-destroy'); +const Api = require('lib/services/rest/Api'); class ClipperServer { @@ -22,6 +13,7 @@ class ClipperServer { this.startState_ = 'idle'; this.server_ = null; this.port_ = null; + this.api_ = new Api(); } static instance() { @@ -32,6 +24,7 @@ class ClipperServer { setLogger(l) { this.logger_ = l; + this.api_.setLogger(l); } logger() { @@ -65,140 +58,6 @@ class ClipperServer { }); } - htmlToMdParser() { - if (this.htmlToMdParser_) return this.htmlToMdParser_; - this.htmlToMdParser_ = new HtmlToMd(); - return this.htmlToMdParser_; - } - - async requestNoteToNote(requestNote) { - const output = { - title: requestNote.title ? requestNote.title : '', - body: requestNote.body ? requestNote.body : '', - }; - - if (requestNote.body_html) { - // Parsing will not work if the HTML is not wrapped in a top level tag, which is not guaranteed - // when getting the content from elsewhere. So here wrap it - it won't change anything to the final - // rendering but it makes sure everything will be parsed. - output.body = await this.htmlToMdParser().parse('
' + requestNote.body_html + '
', { - baseUrl: requestNote.base_url ? requestNote.base_url : '', - }); - } - - if (requestNote.parent_id) { - output.parent_id = requestNote.parent_id; - } else { - const folder = await Folder.defaultFolder(); - if (!folder) throw new Error('Cannot find folder for note'); - output.parent_id = folder.id; - } - - if (requestNote.source_url) output.source_url = requestNote.source_url; - if (requestNote.author) output.author = requestNote.author; - - return output; - } - - // Note must have been saved first - async attachImageFromDataUrl_(note, imageDataUrl, cropRect) { - const tempDir = Setting.value('tempDir'); - const mime = mimeUtils.fromDataUrl(imageDataUrl); - let ext = mimeUtils.toFileExtension(mime) || ''; - if (ext) ext = '.' + ext; - const tempFilePath = tempDir + '/' + md5(Math.random() + '_' + Date.now()) + ext; - const imageConvOptions = {}; - if (cropRect) imageConvOptions.cropRect = cropRect; - await shim.imageFromDataUrl(imageDataUrl, tempFilePath, imageConvOptions); - return await shim.attachFileToNote(note, tempFilePath); - } - - async downloadImage_(url) { - const tempDir = Setting.value('tempDir'); - - const isDataUrl = url && url.toLowerCase().indexOf('data:') === 0; - - const name = isDataUrl ? md5(Math.random() + '_' + Date.now()) : filename(url); - let fileExt = isDataUrl ? mimeUtils.toFileExtension(mimeUtils.fromDataUrl(url)) : safeFileExtension(fileExtension(url).toLowerCase()); - if (fileExt) fileExt = '.' + fileExt; - let imagePath = tempDir + '/' + safeFilename(name) + fileExt; - if (await shim.fsDriver().exists(imagePath)) imagePath = tempDir + '/' + safeFilename(name) + '_' + md5(Math.random() + '_' + Date.now()).substr(0,10) + fileExt; - - try { - if (isDataUrl) { - await shim.imageFromDataUrl(url, imagePath); - } else { - await shim.fetchBlob(url, { path: imagePath }); - } - return imagePath; - } catch (error) { - this.logger().warn('Cannot download image at ' + url, error); - return ''; - } - } - - async downloadImages_(urls) { - const PromisePool = require('es6-promise-pool') - - const output = {}; - - let urlIndex = 0; - const promiseProducer = () => { - if (urlIndex >= urls.length) return null; - - const url = urls[urlIndex++]; - - return new Promise(async (resolve, reject) => { - const imagePath = await this.downloadImage_(url); - if (imagePath) output[url] = { path: imagePath }; - resolve(); - }); - } - - const concurrency = 3 - const pool = new PromisePool(promiseProducer, concurrency) - await pool.start() - - return output; - } - - async createResourcesFromPaths_(urls) { - for (let url in urls) { - if (!urls.hasOwnProperty(url)) continue; - const urlInfo = urls[url]; - try { - const resource = await shim.createResourceFromPath(urlInfo.path); - urlInfo.resource = resource; - } catch (error) { - this.logger().warn('Cannot create resource for ' + url, error); - } - } - return urls; - } - - async removeTempFiles_(urls) { - for (let url in urls) { - if (!urls.hasOwnProperty(url)) continue; - const urlInfo = urls[url]; - try { - await shim.fsDriver().remove(urlInfo.path); - } catch (error) { - this.logger().warn('Cannot remove ' + urlInfo.path, error); - } - } - } - - replaceImageUrlsByResources_(md, urls) { - let output = md.replace(/(!\[.*?\]\()([^\s\)]+)(.*?\))/g, (match, before, imageUrl, after) => { - const urlInfo = urls[imageUrl]; - if (!urlInfo || !urlInfo.resource) return before + imageUrl + after; - const resourceUrl = Resource.internalUrl(urlInfo.resource); - return before + resourceUrl + after; - }); - - return output; - } - async findAvailablePort() { const tcpPortUsed = require('tcp-port-used'); @@ -251,26 +110,33 @@ class ClipperServer { response.end(); } - const requestId = Date.now(); - this.logger().info('Request (' + requestId + '): ' + request.method + ' ' + request.url); + const writeResponse = (code, response) => { + if (typeof response === 'string') { + writeResponseText(code, response); + } else { + writeResponseJson(code, response); + } + } + + this.logger().info('Request: ' + request.method + ' ' + request.url); const url = urlParser.parse(request.url, true); - if (request.method === 'GET') { - if (url.pathname === '/ping') { - return writeResponseText(200, 'JoplinClipperServer'); + const execRequest = async (request, body = '') => { + try { + const response = await this.api_.route(request.method, url.pathname, url.query, body); + writeResponse(200, response); + } catch (error) { + console.error(error); + writeResponse(error.httpCode ? error.httpCode : 500, error.message); } + } - if (url.pathname === '/folders') { - const structure = await Folder.allAsTree({ fields: ['id', 'parent_id', 'title'] }); - return writeResponseJson(200, structure); - } - - if (url.pathname === '/tags') { - return writeResponseJson(200, await Tag.all({ fields: ['id', 'title'] })); - } - } else if (request.method === 'POST') { - if (url.pathname === '/notes') { + if (request.method === 'OPTIONS') { + writeCorsHeaders(200); + response.end(); + } else { + if (request.method === 'POST') { let body = ''; request.on('data', (data) => { @@ -278,57 +144,14 @@ class ClipperServer { }); request.on('end', async () => { - try { - const requestNote = JSON.parse(body); - let note = await this.requestNoteToNote(requestNote); - - const imageUrls = markdownUtils.extractImageUrls(note.body); - - this.logger().info('Request (' + requestId + '): Downloading images: ' + imageUrls.length); - - let result = await this.downloadImages_(imageUrls); - - this.logger().info('Request (' + requestId + '): Creating resources from paths: ' + Object.getOwnPropertyNames(result).length); - - result = await this.createResourcesFromPaths_(result); - await this.removeTempFiles_(result); - note.body = this.replaceImageUrlsByResources_(note.body, result); - - this.logger().info('Request (' + requestId + '): Saving note...'); - - note = await Note.save(note); - - if (requestNote.tags) { - const tagTitles = requestNote.tags.split(','); - await Tag.setNoteTagsByTitles(note.id, tagTitles); - } - - if (requestNote.image_data_url) { - await this.attachImageFromDataUrl_(note, requestNote.image_data_url, requestNote.crop_rect); - } - - this.logger().info('Request (' + requestId + '): Created note ' + note.id); - return writeResponseJson(200, note); - } catch (error) { - this.logger().error(error); - return writeResponseJson(400, { errorCode: 'exception', errorMessage: error.message }); - } + execRequest(request, body); }); } else { - return writeResponseJson(404, { errorCode: 'not_found' }); + execRequest(request); } - } else if (request.method === 'OPTIONS') { - writeCorsHeaders(200); - response.end(); - } else { - return writeResponseJson(405, { errorCode: 'method_not_allowed' }); } }); - this.server_.on('close', () => { - - }); - enableServerDestroy(this.server_); this.logger().info('Starting Clipper server on port ' + this.port_); diff --git a/ReactNativeClient/lib/services/rest/Api.js b/ReactNativeClient/lib/services/rest/Api.js new file mode 100644 index 000000000..4a34a0634 --- /dev/null +++ b/ReactNativeClient/lib/services/rest/Api.js @@ -0,0 +1,288 @@ +const { ltrimSlashes } = require('lib/path-utils.js'); +const Folder = require('lib/models/Folder'); +const Note = require('lib/models/Note'); +const Tag = require('lib/models/Tag'); +const Setting = require('lib/models/Setting'); +const markdownUtils = require('lib/markdownUtils'); +const mimeUtils = require('lib/mime-utils.js').mime; +const { Logger } = require('lib/logger.js'); +const md5 = require('md5'); +const { shim } = require('lib/shim'); +const HtmlToMd = require('lib/HtmlToMd'); +const { fileExtension, safeFileExtension, safeFilename, filename } = require('lib/path-utils'); + +class ApiError extends Error { + + constructor(message, httpCode = 400) { + super(message); + this.httpCode_ = httpCode; + } + + get httpCode() { + return this.httpCode_; + } + +} + +class MethodNotAllowedError extends ApiError { + + constructor() { + super('Method Not Allowed', 405); + } + +} + +class NotFoundError extends ApiError { + + constructor() { + super('Not Found', 404); + } + +} + +class Api { + + constructor() { + this.logger_ = new Logger(); + } + + async route(method, path, query = null, body = null) { + path = ltrimSlashes(path); + const callName = 'action_' + path; + if (!this[callName]) throw new NotFoundError(); + + try { + return this[callName]({ + method: method, + query: query ? query : {}, + body: body, + }); + } catch (error) { + if (!error.httpCode) error.httpCode = 500; + throw error; + } + } + + setLogger(l) { + this.logger_ = l; + } + + logger() { + return this.logger_; + } + + fields_(request, defaultFields) { + const query = request.query; + if (!query || !query.fields) return defaultFields; + const fields = query.fields.split(',').map(f => f.trim()).filter(f => !!f); + return fields.length ? fields : defaultFields; + } + + async action_ping(request) { + if (request.method === 'GET') { + return 'JoplinClipperServer'; + } + throw new MethodNotAllowedError(); + } + + async action_folders(request) { + if (request.method === 'GET') { + return await Folder.allAsTree({ fields: this.fields_(request, ['id', 'parent_id', 'title']) }); + } + + throw new MethodNotAllowedError(); + } + + async action_tags(request) { + if (request.method === 'GET') { + return await Tag.all({ fields: this.fields_(request, ['id', 'title']) }) + } + + throw new MethodNotAllowedError(); + } + + async action_notes(request) { + if (request.method === 'POST') { + const requestId = Date.now(); + const requestNote = JSON.parse(request.body); + let note = await this.requestNoteToNote(requestNote); + + const imageUrls = markdownUtils.extractImageUrls(note.body); + + this.logger().info('Request (' + requestId + '): Downloading images: ' + imageUrls.length); + + let result = await this.downloadImages_(imageUrls); + + this.logger().info('Request (' + requestId + '): Creating resources from paths: ' + Object.getOwnPropertyNames(result).length); + + result = await this.createResourcesFromPaths_(result); + await this.removeTempFiles_(result); + note.body = this.replaceImageUrlsByResources_(note.body, result); + + this.logger().info('Request (' + requestId + '): Saving note...'); + + note = await Note.save(note); + + if (requestNote.tags) { + const tagTitles = requestNote.tags.split(','); + await Tag.setNoteTagsByTitles(note.id, tagTitles); + } + + if (requestNote.image_data_url) { + note = await this.attachImageFromDataUrl_(note, requestNote.image_data_url, requestNote.crop_rect); + } + + this.logger().info('Request (' + requestId + '): Created note ' + note.id); + + return note; + } + + throw new MethodNotAllowedError(); + } + + + + + + // ======================================================================================================================== + // UTILIY FUNCTIONS + // ======================================================================================================================== + + htmlToMdParser() { + if (this.htmlToMdParser_) return this.htmlToMdParser_; + this.htmlToMdParser_ = new HtmlToMd(); + return this.htmlToMdParser_; + } + + async requestNoteToNote(requestNote) { + const output = { + title: requestNote.title ? requestNote.title : '', + body: requestNote.body ? requestNote.body : '', + }; + + if (requestNote.body_html) { + // Parsing will not work if the HTML is not wrapped in a top level tag, which is not guaranteed + // when getting the content from elsewhere. So here wrap it - it won't change anything to the final + // rendering but it makes sure everything will be parsed. + output.body = await this.htmlToMdParser().parse('
' + requestNote.body_html + '
', { + baseUrl: requestNote.base_url ? requestNote.base_url : '', + }); + } + + if (requestNote.parent_id) { + output.parent_id = requestNote.parent_id; + } else { + const folder = await Folder.defaultFolder(); + if (!folder) throw new Error('Cannot find folder for note'); + output.parent_id = folder.id; + } + + if (requestNote.source_url) output.source_url = requestNote.source_url; + if (requestNote.author) output.author = requestNote.author; + + return output; + } + + // Note must have been saved first + async attachImageFromDataUrl_(note, imageDataUrl, cropRect) { + const tempDir = Setting.value('tempDir'); + const mime = mimeUtils.fromDataUrl(imageDataUrl); + let ext = mimeUtils.toFileExtension(mime) || ''; + if (ext) ext = '.' + ext; + const tempFilePath = tempDir + '/' + md5(Math.random() + '_' + Date.now()) + ext; + const imageConvOptions = {}; + if (cropRect) imageConvOptions.cropRect = cropRect; + await shim.imageFromDataUrl(imageDataUrl, tempFilePath, imageConvOptions); + return await shim.attachFileToNote(note, tempFilePath); + } + + async downloadImage_(url) { + const tempDir = Setting.value('tempDir'); + + const isDataUrl = url && url.toLowerCase().indexOf('data:') === 0; + + const name = isDataUrl ? md5(Math.random() + '_' + Date.now()) : filename(url); + let fileExt = isDataUrl ? mimeUtils.toFileExtension(mimeUtils.fromDataUrl(url)) : safeFileExtension(fileExtension(url).toLowerCase()); + if (fileExt) fileExt = '.' + fileExt; + let imagePath = tempDir + '/' + safeFilename(name) + fileExt; + if (await shim.fsDriver().exists(imagePath)) imagePath = tempDir + '/' + safeFilename(name) + '_' + md5(Math.random() + '_' + Date.now()).substr(0,10) + fileExt; + + try { + if (isDataUrl) { + await shim.imageFromDataUrl(url, imagePath); + } else { + await shim.fetchBlob(url, { path: imagePath }); + } + return imagePath; + } catch (error) { + this.logger().warn('Cannot download image at ' + url, error); + return ''; + } + } + + async downloadImages_(urls) { + const PromisePool = require('es6-promise-pool') + + const output = {}; + + let urlIndex = 0; + const promiseProducer = () => { + if (urlIndex >= urls.length) return null; + + const url = urls[urlIndex++]; + + return new Promise(async (resolve, reject) => { + const imagePath = await this.downloadImage_(url); + if (imagePath) output[url] = { path: imagePath }; + resolve(); + }); + } + + const concurrency = 3 + const pool = new PromisePool(promiseProducer, concurrency) + await pool.start() + + return output; + } + + async createResourcesFromPaths_(urls) { + for (let url in urls) { + if (!urls.hasOwnProperty(url)) continue; + const urlInfo = urls[url]; + try { + const resource = await shim.createResourceFromPath(urlInfo.path); + urlInfo.resource = resource; + } catch (error) { + this.logger().warn('Cannot create resource for ' + url, error); + } + } + return urls; + } + + async removeTempFiles_(urls) { + for (let url in urls) { + if (!urls.hasOwnProperty(url)) continue; + const urlInfo = urls[url]; + try { + await shim.fsDriver().remove(urlInfo.path); + } catch (error) { + this.logger().warn('Cannot remove ' + urlInfo.path, error); + } + } + } + + replaceImageUrlsByResources_(md, urls) { + let output = md.replace(/(!\[.*?\]\()([^\s\)]+)(.*?\))/g, (match, before, imageUrl, after) => { + const urlInfo = urls[imageUrl]; + if (!urlInfo || !urlInfo.resource) return before + imageUrl + after; + const resourceUrl = Resource.internalUrl(urlInfo.resource); + return before + resourceUrl + after; + }); + + return output; + } + + +} + +module.exports = Api; \ No newline at end of file diff --git a/ReactNativeClient/lib/shim-init-node.js b/ReactNativeClient/lib/shim-init-node.js index 8d4836d0e..70eb4aff3 100644 --- a/ReactNativeClient/lib/shim-init-node.js +++ b/ReactNativeClient/lib/shim-init-node.js @@ -186,7 +186,11 @@ function shimInit() { const mime = mimeUtils.fromDataUrl(imageDataUrl); await shim.writeImageToFile(image, mime, filePath); } else { - throw new Error('Node support not implemented'); + if (options.cropRect) throw new Error('Crop rect not supported in Node'); + + const imageDataURI = require('image-data-uri'); + const result = imageDataURI.decode(imageDataUrl); + await shim.fsDriver().writeFile(filePath, result.dataBuffer, 'buffer'); } }