From df2313ee3a687518b13c7a3c0abab9f22d865eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tadeusz=20So=C5=9Bnierz?= Date: Wed, 8 Jul 2020 10:38:06 +0200 Subject: [PATCH 1/3] Hardwire xapian and emscripten versions to make sure the index builds again Somewhen between June 2019 and now one of these components doesn't produce a working wasm anymore (complains about missing IDBFS). Preferably we'd track down exactly when and why this happens (and fix it), but for now let's at least have a working build. --- .travis.yml | 19 +++++++------------ build-xapian.sh | 14 ++++++++++++++ compilermmxapianapi.js | 4 ++-- setup-emsdk.sh | 6 ++++++ 4 files changed, 29 insertions(+), 14 deletions(-) create mode 100755 build-xapian.sh create mode 100755 setup-emsdk.sh diff --git a/.travis.yml b/.travis.yml index 392d138..cccbac4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ os: linux addons: apt: packages: + - build-essential - doxygen - graphviz - help2man @@ -15,16 +16,10 @@ services: - docker before_script: # Bootstrap only xapian-core for emscripten build. - - git clone https://github.com/xapian/xapian - - cd xapian - - ./bootstrap xapian-core - - ./configure CXXFLAGS=-O0 --disable-backend-honey --disable-backend-inmemory --disable-backend-remote - - make -j2 - - make -j2 distclean - - cd xapian-core - - docker run -v $(pwd):/src trzeci/emscripten emconfigure ./configure CPPFLAGS='-DFLINTLOCK_USE_FLOCK' CXXFLAGS='-Oz -s USE_ZLIB=1 -fno-rtti' --disable-backend-honey --disable-backend-inmemory --disable-shared --disable-backend-remote - - docker run -v $(pwd):/src trzeci/emscripten emmake make - - cd ../.. + - ./setup-emsdk.sh + - source ./emsdk/emsdk_env.sh + - ./build-xapian.sh script: - - docker run -v $(pwd):/src trzeci/emscripten node compilermmxapianapi.js --xapiandir=xapian/xapian-core - - npm run test-no-watch \ No newline at end of file + - source ./emsdk/emsdk_env.sh + - node compilermmxapianapi.js --xapiandir=xapian/xapian-core + - npm run test-no-watch diff --git a/build-xapian.sh b/build-xapian.sh new file mode 100755 index 0000000..6daf5ae --- /dev/null +++ b/build-xapian.sh @@ -0,0 +1,14 @@ +#!/bin/bash +THREADS=${THREADS:-2} + +git clone https://github.com/xapian/xapian +cd xapian +git checkout v1.4.16 +./bootstrap xapian-core +./configure CXXFLAGS=-O0 --disable-backend-honey --disable-backend-inmemory --disable-backend-remote +make -j$THREADS +make -j$THREADS distclean +cd xapian-core +emconfigure ./configure CPPFLAGS='-DFLINTLOCK_USE_FLOCK' CXXFLAGS='-Oz -s USE_ZLIB=1 -fno-rtti' --disable-backend-honey --disable-backend-inmemory --disable-shared --disable-backend-remote +emmake make -j$THREADS +cd ../.. diff --git a/compilermmxapianapi.js b/compilermmxapianapi.js index 8e548d0..5394afd 100644 --- a/compilermmxapianapi.js +++ b/compilermmxapianapi.js @@ -21,8 +21,8 @@ if(!process.env.XAPIAN) { execSync(`em++ -Oz -s DISABLE_EXCEPTION_CATCHING=0 -s USE_ZLIB=1 ` + `-s "EXTRA_EXPORTED_RUNTIME_METHODS=['FS','cwrap','stringToUTF8','UTF8ToString','getValue']" ` + `-std=c++11 -s DEMANGLE_SUPPORT=1 -s ALLOW_MEMORY_GROWTH=1 ` + - `-I$XAPIAN/include -I$XAPIAN -I$XAPIAN/common rmmxapianapi.cc $XAPIAN/.libs/libxapian-1.5.a ` + - `-o dist/xapianasm.js`, { stdio: 'inherit' }); + `-I$XAPIAN/include -I$XAPIAN -I$XAPIAN/common rmmxapianapi.cc $XAPIAN/.libs/libxapian.a ` + + `-o dist/xapianasm.js -lidbfs.js`, { stdio: 'inherit' }); console.log('Successful build of xapianasm.wasm and xapianasm.js'); } catch(e) { console.error('Compile failed'); diff --git a/setup-emsdk.sh b/setup-emsdk.sh new file mode 100755 index 0000000..8f74dfa --- /dev/null +++ b/setup-emsdk.sh @@ -0,0 +1,6 @@ +#!/bin/bash +git clone https://github.com/emscripten-core/emsdk.git +cd emsdk +./emsdk install 1.39.19 +./emsdk activate 1.39.19 +cd .. From be76a2254806f083f0a2cf45999c89ce7b6e1558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tadeusz=20So=C5=9Bnierz?= Date: Thu, 6 Aug 2020 17:28:15 +0200 Subject: [PATCH 2/3] Eliminate the (circular) runbox7lib dependency From now on, this package will be the home of all the relevant modules, hopefully making the maintenance easier. The tests have also been migrated to @testdeck/mocha, which is apparently a successor to mocha-typescript. --- .gitignore | 4 +- package-lock.json | 1191 ++++++++++++----- package.json | 13 +- ts/{xapian => test}/changefolder.test.ts | 14 +- ts/test/mailaddressinfo.test.ts | 61 + ts/test/messageinfo.test.ts | 60 + ts/{xapian => test}/modifydocterms.test.ts | 14 +- ts/{xapian => test}/search.test.ts | 11 +- ts/test/test.ts | 11 +- ts/{xapian => test}/xapian.test.ts | 15 +- ts/xapian/downloadablesearchindexmap.class.ts | 35 + ts/xapian/index.ts | 8 + ts/xapian/mailaddressinfo.ts | 81 ++ ts/xapian/messageinfo.ts | 172 +++ ts/xapian/rmmxapianapi.ts | 274 ++++ tsconfig.json | 2 + 16 files changed, 1594 insertions(+), 372 deletions(-) rename ts/{xapian => test}/changefolder.test.ts (92%) create mode 100644 ts/test/mailaddressinfo.test.ts create mode 100644 ts/test/messageinfo.test.ts rename ts/{xapian => test}/modifydocterms.test.ts (97%) rename ts/{xapian => test}/search.test.ts (92%) rename ts/{xapian => test}/xapian.test.ts (97%) create mode 100644 ts/xapian/downloadablesearchindexmap.class.ts create mode 100644 ts/xapian/index.ts create mode 100644 ts/xapian/mailaddressinfo.ts create mode 100644 ts/xapian/messageinfo.ts create mode 100644 ts/xapian/rmmxapianapi.ts diff --git a/.gitignore b/.gitignore index d297d0d..229da9e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ build node_modules xapianasm.* .vscode -dist \ No newline at end of file +dist +/xapian/ +/emsdk/ diff --git a/package-lock.json b/package-lock.json index 3f16a72..c0df357 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,16 +4,37 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@types/mocha": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.5.tgz", - "integrity": "sha512-lAVp+Kj54ui/vLUFxsJTMtWvZraZxum3w3Nwkble2dNuV5VnPA+Mi2oGX9XYJAaIvZi3tn3cbjS/qcJXRb6Bww==", + "@testdeck/core": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@testdeck/core/-/core-0.1.0.tgz", + "integrity": "sha512-gJj/HQoNUtyb5B/KC1UcirYAn8bv6XLzlFIIIFfoR7Ew5OQcU7UNbjwJUKNtJ27YtVTw1a1dNadk6RQts/1TCw==", + "dev": true + }, + "@testdeck/mocha": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@testdeck/mocha/-/mocha-0.1.0.tgz", + "integrity": "sha512-1kE5AlkfDCgIrwjCRLtPam4YSo0MUf3Wj95mcVkRtRdExnQOvX3ravFrEYBRsfcmlsFPX7WsQFi0sje2cSzvnQ==", + "dev": true, + "requires": { + "@testdeck/core": "^0.1.0" + } + }, + "@types/chai": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.12.tgz", + "integrity": "sha512-aN5IAC8QNtSUdQzxu7lGBgYAOuU1tmRU4c9dIq5OKGf/SBVjXo+ffM2wEjudAWbgpOhy60nLoAGH1xm8fpCKFQ==", "dev": true }, "@types/node": { - "version": "10.12.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", - "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==", + "version": "10.17.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.28.tgz", + "integrity": "sha512-dzjES1Egb4c1a89C7lKwQh8pwjYmlOAG9dW1pBgxEk57tMrLnssOfEthz8kdkNaBd7lIqQx7APm5+mZ619IiCQ==", + "dev": true + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, "ansi-regex": { @@ -31,12 +52,55 @@ "color-convert": "^1.9.0" } }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array.prototype.map": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz", + "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.4" + } + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "dev": true + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -47,6 +111,15 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -54,11 +127,25 @@ "dev": true }, "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + } + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -68,24 +155,79 @@ "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" + }, + "dependencies": { + "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" + } + } } }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "chokidar": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", + "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.3.0" } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } }, "color-convert": { "version": "1.9.3", @@ -102,38 +244,19 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "decamelize": { @@ -142,53 +265,125 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "es-get-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", + "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", + "dev": true, + "requires": { + "es-abstract": "^1.17.4", + "has-symbols": "^1.0.1", + "is-arguments": "^1.0.4", + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - } + "to-regex-range": "^5.0.1" } }, "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", "dev": true, "requires": { - "locate-path": "^2.0.0" + "is-buffer": "~2.0.3" } }, "fs.realpath": { @@ -197,22 +392,35 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -223,22 +431,46 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, "inflight": { @@ -252,15 +484,48 @@ } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", + "dev": true + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, "is-fullwidth-code-point": { @@ -269,10 +534,67 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, - "is-stream": { + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", + "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-set": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", + "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", + "dev": true + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true }, "isexe": { @@ -281,50 +603,50 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "iterate-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz", + "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==", + "dev": true + }, + "iterate-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", + "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", "dev": true, "requires": { - "invert-kv": "^1.0.0" + "es-get-iterator": "^1.0.2", + "iterate-iterator": "^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=", + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "p-locate": "^4.1.0" } }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "chalk": "^2.4.2" } }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -334,90 +656,75 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.1.tgz", + "integrity": "sha512-p7FuGlYH8t7gaiodlFreseLxEmxTgvyG9RgPHODFPySNhwUehu8NIb0vdSt3WFckSneswZ0Un5typYcWElk7HQ==", "dev": true, "requires": { + "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", - "diff": "3.5.0", + "chokidar": "3.3.1", + "debug": "3.2.6", + "diff": "4.0.2", "escape-string-regexp": "1.0.5", - "glob": "7.1.2", + "find-up": "4.1.0", + "glob": "7.1.6", "growl": "1.10.5", - "he": "1.1.1", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "3.0.0", "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" - }, - "dependencies": { - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "mocha-typescript": { - "version": "1.1.17", - "resolved": "https://registry.npmjs.org/mocha-typescript/-/mocha-typescript-1.1.17.tgz", - "integrity": "sha512-Ge6pCQkZumkkhxVNdAf3JxunskShgaynCb30HYD7TT1Yhog/7NW2+6w5RcRHI+nuQrCMTX6z1+qf2pD8qwCoQA==", - "dev": true, - "requires": { - "@types/mocha": "^5.2.0", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "yargs": "^11.0.0" + "ms": "2.1.2", + "object.assign": "4.1.0", + "promise.allsettled": "1.0.2", + "serialize-javascript": "4.0.0", + "strip-json-comments": "3.0.1", + "supports-color": "7.1.0", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.0.0", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "1.6.1" } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "dev": true, "requires": { - "path-key": "^2.0.0" + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -427,51 +734,34 @@ "wrappy": "1" } }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dev": true, - "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { - "p-try": "^1.0.0" + "p-try": "^2.0.0" } }, "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "^2.2.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=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, "path-is-absolute": { @@ -480,18 +770,49 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", "dev": true }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, + "promise.allsettled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz", + "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==", + "dev": true, + "requires": { + "array.prototype.map": "^1.0.1", + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "iterate-value": "^1.0.0" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readdirp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", + "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", + "dev": true, + "requires": { + "picomatch": "^2.0.7" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -499,55 +820,45 @@ "dev": true }, "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "runbox7lib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/runbox7lib/-/runbox7lib-0.2.0.tgz", - "integrity": "sha512-B4lKz3uGWUb8q8S0k9P7Vs+TC64lZ+Va8Xf7u/BeuGCvpBEhTI0Gd8rCk9awBEjfFfjImJ3y4Ni5pyUCeQ3dBQ==" - }, "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz", + "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==", + "dev": true, "requires": { "tslib": "^1.9.0" } }, - "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "randombytes": "^2.1.0" } }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, "string-width": { @@ -560,6 +871,26 @@ "strip-ansi": "^4.0.0" } }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -569,36 +900,60 @@ "ansi-regex": "^3.0.0" } }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", "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==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + } + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" } }, "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true }, "typescript": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz", - "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==", + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", + "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", "dev": true }, "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -610,49 +965,56 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "workerpool": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", + "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==", + "dev": true + }, "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" }, "dependencies": { "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" } }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^4.1.0" } } } @@ -664,44 +1026,203 @@ "dev": true }, "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, "yargs": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", - "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", + "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", - "string-width": "^2.0.0", + "string-width": "^3.0.0", "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, "yargs-parser": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", - "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yargs-unparser": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz", + "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "^5.3.1", + "decamelize": "^1.2.0", + "flat": "^4.1.0", + "is-plain-obj": "^1.1.0", + "yargs": "^14.2.3" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "yargs": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", + "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + } + }, + "yargs-parser": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", + "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } } } } diff --git a/package.json b/package.json index 57b852c..1e0bbbd 100644 --- a/package.json +++ b/package.json @@ -2,17 +2,18 @@ "name": "runbox-searchindex", "version": "0.1.0", "scripts": { - "build": "node compilermmxapianapi.js", + "build": "node compilermmxapianapi.js --xapiandir=xapian/xapian-core && tsc", "test": "mocha-typescript-watch -p tsconfig.json build/test/test.js", "test-no-watch": "tsc -p tsconfig.json && mocha build/test/test.js", - "preparelib": "node preparelibrary.js" + "preparelib": "cp -r build/xapian/* dist/ && node preparelibrary.js" }, "devDependencies": { - "runbox7lib": "^0.2.0", - "rxjs": "^6.3.3", + "@testdeck/mocha": "0.1.0", + "@types/chai": "^4.2.12", "@types/node": "^10.12.18", - "mocha": "^5.2.0", - "mocha-typescript": "^1.1.17", + "chai": "^4.2.0", + "mocha": "^8.1.1", + "rxjs": "^6.3.3", "typescript": "^3.2.2" } } diff --git a/ts/xapian/changefolder.test.ts b/ts/test/changefolder.test.ts similarity index 92% rename from ts/xapian/changefolder.test.ts rename to ts/test/changefolder.test.ts index 1ff4ec2..104cc78 100644 --- a/ts/xapian/changefolder.test.ts +++ b/ts/test/changefolder.test.ts @@ -1,12 +1,12 @@ -import { suite, test, only, slow, timeout } from 'mocha-typescript'; -import { equal, ok } from 'assert'; +import { suite, test } from "@testdeck/mocha"; +import { equal } from 'assert'; -import { existsSync, unlinkSync, watch, mkdirSync } from 'fs'; -import { execSync, ChildProcess } from 'child_process'; +import { execSync } from 'child_process'; -import { loadXapian } from './xapian.loader'; -import { XapianAPI } from 'runbox7lib'; -import { IndexingTools, MessageInfo, MailAddressInfo } from 'runbox7lib'; +import { loadXapian } from '../xapian/xapian.loader'; +import { XapianAPI } from '../xapian/rmmxapianapi'; +import { IndexingTools, MessageInfo } from '../xapian/messageinfo'; +import { MailAddressInfo } from '../xapian/mailaddressinfo'; const XAPIANFSTYPE: string = 'MEM'; diff --git a/ts/test/mailaddressinfo.test.ts b/ts/test/mailaddressinfo.test.ts new file mode 100644 index 0000000..5879eb5 --- /dev/null +++ b/ts/test/mailaddressinfo.test.ts @@ -0,0 +1,61 @@ +import { MailAddressInfo } from '../xapian/mailaddressinfo'; + +import { suite, test } from "@testdeck/mocha"; +import { expect } from 'chai'; + +@suite export class MailAddressInfoTest { + @test constructWithNameAndEmail() { + const ma = new MailAddressInfo('Test', 'test1@runbox.com'); + expect(ma.name).to.be.equal('Test'); + expect(ma.address).to.be.equal('test1@runbox.com'); + expect(ma.nameAndAddress).to.be.equal('"Test" '); + } + + @test parseSingleEmailAddress() { + const ma = MailAddressInfo.parse('test1@runbox.com'); + expect(ma[0].name).to.be.equal(null); + expect(ma[0].address).to.be.equal('test1@runbox.com'); + expect(ma[0].nameAndAddress).to.be.equal('test1@runbox.com'); + } + + @test parseFullSingleAddress() { + const ma = MailAddressInfo.parse('"Test" '); + expect(ma[0].name).to.be.equal('Test'); + expect(ma[0].address).to.be.equal('test1@runbox.com'); + expect(ma[0].nameAndAddress).to.be.equal('"Test" '); + } + + @test parseFullSingleAddressNoQuotes() { + const ma = MailAddressInfo.parse('Test '); + expect(ma[0].name).to.be.equal('Test'); + expect(ma[0].address).to.be.equal('test1@runbox.com'); + expect(ma[0].nameAndAddress).to.be.equal('"Test" '); + } + + @test parseAddressList() { + const ma_list = MailAddressInfo.parse('test1@runbox.com,test2@runbox.com'); + expect(ma_list[0].name).to.be.equal(null); + expect(ma_list[0].address).to.be.equal('test1@runbox.com'); + expect(ma_list[0].nameAndAddress).to.be.equal('test1@runbox.com'); + expect(ma_list[1].name).to.be.equal(null); + expect(ma_list[1].address).to.be.equal('test2@runbox.com'); + expect(ma_list[1].nameAndAddress).to.be.equal('test2@runbox.com'); + } + + @test parseEmptyNamesAddress() { + const ma_list = MailAddressInfo.parse('"" '); + expect(ma_list[0].name).to.be.equal(''); + expect(ma_list[0].address).to.be.equal('test1@runbox.com'); + expect(ma_list[0].nameAndAddress).to.be.equal('test1@runbox.com'); + } + + @test parseFullAddressList() { + const ma_list = MailAddressInfo.parse('"Test1" , "Test2" '); + expect(ma_list[0].name).to.be.equal('Test1'); + expect(ma_list[0].address).to.be.equal('test1@runbox.com'); + expect(ma_list[0].nameAndAddress).to.be.equal('"Test1" '); + expect(ma_list[1].name).to.be.equal('Test2'); + expect(ma_list[1].address).to.be.equal('test2@runbox.com'); + expect(ma_list[1].nameAndAddress).to.be.equal('"Test2" '); + } +} diff --git a/ts/test/messageinfo.test.ts b/ts/test/messageinfo.test.ts new file mode 100644 index 0000000..bd78881 --- /dev/null +++ b/ts/test/messageinfo.test.ts @@ -0,0 +1,60 @@ +import { MessageInfo, IndexingTools } from '../xapian/messageinfo'; +import { MailAddressInfo } from '../xapian/mailaddressinfo'; + +import { suite, test } from "@testdeck/mocha"; +import { expect } from 'chai'; + +@suite export class MessageInfoTest { + @test testGetSubjectWithoutAbbreviation() { + expect(MessageInfo.getSubjectWithoutAbbreviation('Re: Testing the subject')).to.equal('Testing the subject'); + expect(MessageInfo.getSubjectWithoutAbbreviation('FWD: Testing the subject')).to.equal('Testing the subject'); + expect(MessageInfo.getSubjectWithoutAbbreviation('Re: Fwd: Testing the subject')).to.equal('Testing the subject'); + expect(MessageInfo.getSubjectWithoutAbbreviation('SV: Fwd: Test FWD: svar')).to.equal('Test FWD: svar'); + expect(MessageInfo.getSubjectWithoutAbbreviation('')).to.equal(''); + expect(MessageInfo.getSubjectWithoutAbbreviation(null)).to.equal(''); + } + + @test testAddMessageToIndexWithDeleteFolders() { + console.log(`Testing that messages added to specified folders will be deleted`); + const msg = new MessageInfo(1, + new Date(), + new Date(), + 'Spam', + false, + false, + false, + MailAddressInfo.parse('test@example.com'), + MailAddressInfo.parse('test2@example.com'), + [], + [], + 'Test subject', + 'The text', + 50, + false + ); + + let addCalled = false; + let removeCalled = false; + const indexingtools = new IndexingTools({ + addSortableEmailToXapianIndex: () => { + addCalled = true; + }, + + deleteDocumentByUniqueTerm: () => { + removeCalled = true; + } + } as any); + + indexingtools.addMessageToIndex(msg); + expect(addCalled).to.be.true; + addCalled = false; + indexingtools.addMessageToIndex(msg, ['Trash', 'Spam']); + expect(removeCalled).to.be.true; + removeCalled = false; + msg.folder = 'Inbox'; + + indexingtools.addMessageToIndex(msg, ['Trash', 'Spam']); + expect(removeCalled).to.be.false; + expect(addCalled).to.be.true; + } +} diff --git a/ts/xapian/modifydocterms.test.ts b/ts/test/modifydocterms.test.ts similarity index 97% rename from ts/xapian/modifydocterms.test.ts rename to ts/test/modifydocterms.test.ts index 6e57c62..801dc71 100644 --- a/ts/xapian/modifydocterms.test.ts +++ b/ts/test/modifydocterms.test.ts @@ -1,11 +1,13 @@ -import { suite, test } from 'mocha-typescript'; -import { equal } from 'assert'; - import { execSync } from 'child_process'; -import { loadXapian } from './xapian.loader'; -import { XapianAPI } from 'runbox7lib'; -import { IndexingTools, MessageInfo, MailAddressInfo } from 'runbox7lib'; +import { loadXapian } from '../xapian/xapian.loader'; +import { XapianAPI } from '../xapian/rmmxapianapi'; +import { IndexingTools, MessageInfo } from '../xapian/messageinfo'; +import { MailAddressInfo } from '../xapian/mailaddressinfo'; + +import { suite, test } from "@testdeck/mocha"; +import { equal } from 'assert'; + const XAPIANFSTYPE: string = 'MEM'; diff --git a/ts/xapian/search.test.ts b/ts/test/search.test.ts similarity index 92% rename from ts/xapian/search.test.ts rename to ts/test/search.test.ts index 4e8da43..ad8b40e 100644 --- a/ts/xapian/search.test.ts +++ b/ts/test/search.test.ts @@ -1,8 +1,9 @@ -import { suite, test, slow, timeout, only } from 'mocha-typescript'; -import { XapianAPI } from 'runbox7lib'; +import { suite, test } from "@testdeck/mocha"; import { equal } from 'assert'; -import { loadXapian } from './xapian.loader'; -import { MailAddressInfo, MessageInfo, IndexingTools } from 'runbox7lib'; +import { loadXapian } from '../xapian/xapian.loader'; +import { XapianAPI } from '../xapian/rmmxapianapi'; +import { IndexingTools, MessageInfo } from '../xapian/messageinfo'; +import { MailAddressInfo } from '../xapian/mailaddressinfo'; declare var FS, MEMFS, Module; @@ -103,4 +104,4 @@ const receivers = [ console.log('recipientterms', global['termlistresult']); equal(5, numterms); } -} \ No newline at end of file +} diff --git a/ts/test/test.ts b/ts/test/test.ts index cadc16b..6459e1f 100644 --- a/ts/test/test.ts +++ b/ts/test/test.ts @@ -1,5 +1,6 @@ -export { XapianTest } from '../xapian/xapian.test'; -export { ChangeFolderTest } from '../xapian/changefolder.test'; -export { ModifyDocTermsTest } from '../xapian/modifydocterms.test'; -export { SearchTest } from '../xapian/search.test'; - +export { XapianTest } from './xapian.test'; +export { ChangeFolderTest } from './changefolder.test'; +export { ModifyDocTermsTest } from './modifydocterms.test'; +export { SearchTest } from './search.test'; +export { MessageInfoTest } from './messageinfo.test'; +export { MailAddressInfoTest } from './mailaddressinfo.test'; diff --git a/ts/xapian/xapian.test.ts b/ts/test/xapian.test.ts similarity index 97% rename from ts/xapian/xapian.test.ts rename to ts/test/xapian.test.ts index 2c79a29..024d7cd 100644 --- a/ts/xapian/xapian.test.ts +++ b/ts/test/xapian.test.ts @@ -1,12 +1,13 @@ -import { suite, test, only, slow, timeout } from 'mocha-typescript'; -import { equal, ok } from 'assert'; +import { suite, test, timeout } from "@testdeck/mocha"; +import { equal } from 'assert'; -import { existsSync, unlinkSync, watch, mkdirSync } from 'fs'; -import { execSync, ChildProcess } from 'child_process'; +import { existsSync, mkdirSync } from 'fs'; +import { execSync } from 'child_process'; -import { loadXapian } from './xapian.loader'; -import { XapianAPI } from 'runbox7lib'; -import { IndexingTools, MessageInfo, MailAddressInfo } from 'runbox7lib'; +import { loadXapian } from '../xapian/xapian.loader'; +import { XapianAPI } from '../xapian/rmmxapianapi'; +import { IndexingTools, MessageInfo } from '../xapian/messageinfo'; +import { MailAddressInfo } from '../xapian/mailaddressinfo'; const XAPIANFSTYPE: string = 'MEM'; // const XAPIANFSTYPE: string = 'NODE'; diff --git a/ts/xapian/downloadablesearchindexmap.class.ts b/ts/xapian/downloadablesearchindexmap.class.ts new file mode 100644 index 0000000..feb3d30 --- /dev/null +++ b/ts/xapian/downloadablesearchindexmap.class.ts @@ -0,0 +1,35 @@ +// --------- BEGIN RUNBOX LICENSE --------- +// Copyright (C) 2016-2018 Runbox Solutions AS (runbox.com). +// +// This file is part of Runbox 7. +// +// Runbox 7 is free software: You can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// Runbox 7 is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Runbox 7. If not, see . +// ---------- END RUNBOX LICENSE ---------- + +export class DownloadablePartitionFile { + filename: string; + compressedsize: number; + uncompressedsize: number; + downloaded?: number; +} + +export class DownloadablePartition { + numberOfMessages: number; + folder: string; + files: DownloadablePartitionFile[]; +} + +export class DownloadableSearchIndexMap { + partitions: DownloadablePartition[] = []; +} diff --git a/ts/xapian/index.ts b/ts/xapian/index.ts new file mode 100644 index 0000000..33ca061 --- /dev/null +++ b/ts/xapian/index.ts @@ -0,0 +1,8 @@ +export { + DownloadablePartitionFile, DownloadablePartition, DownloadableSearchIndexMap +} from './downloadablesearchindexmap.class'; + +export { MailAddressInfo } from './mailaddressinfo'; +export { MessageInfo } from './messageinfo'; +export { XapianAPI, SearchParams } from './rmmxapianapi'; +export { loadXapian } from './xapian.loader'; diff --git a/ts/xapian/mailaddressinfo.ts b/ts/xapian/mailaddressinfo.ts new file mode 100644 index 0000000..bcf8333 --- /dev/null +++ b/ts/xapian/mailaddressinfo.ts @@ -0,0 +1,81 @@ +// --------- BEGIN RUNBOX LICENSE --------- +// Copyright (C) 2016-2018 Runbox Solutions AS (runbox.com). +// +// This file is part of Runbox 7. +// +// Runbox 7 is free software: You can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// Runbox 7 is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Runbox 7. If not, see . +// ---------- END RUNBOX LICENSE ---------- + +export class MailAddressInfo { + nameAndAddress: string; + + constructor(public name: string, public address: string) { + this.nameAndAddress = name ? `"${name}" <${address}>` : address; + } + + public static parse(mailaddr: string): MailAddressInfo[] { + const ret: MailAddressInfo[] = []; + let namePart = false; + let addrPart = false; + let lastStart = 0; + let name: string = null; + let addr: string = null; + for (let n = 0; n < mailaddr.length; n++) { + const ch = mailaddr.charAt(n); + + switch (ch) { + case '"': + namePart = !namePart; + + if (namePart) { + lastStart = n + 1; + } else { + name = mailaddr.substring(lastStart, n).trim(); + } + break; + case ',': + if (!namePart) { + if (!addr) { + addr = mailaddr.substring(lastStart, n).trim(); + } + ret.push(new MailAddressInfo(name, addr)); + addr = null; + name = null; + lastStart = n + 1; + } + break; + case '<': + if (!namePart) { + addrPart = true; + if (name == null) { + name = mailaddr.substring(lastStart, n).trim(); + } + lastStart = n + 1; + } + break; + case '>': + if (!namePart) { + addrPart = false; + addr = mailaddr.substring(lastStart, n).trim(); + } + break; + } + } + if (!addr) { + addr = mailaddr.substring(lastStart, mailaddr.length).trim(); + } + ret.push(new MailAddressInfo(name, addr)); + return ret; + } +} diff --git a/ts/xapian/messageinfo.ts b/ts/xapian/messageinfo.ts new file mode 100644 index 0000000..308372a --- /dev/null +++ b/ts/xapian/messageinfo.ts @@ -0,0 +1,172 @@ +// --------- BEGIN RUNBOX LICENSE --------- +// Copyright (C) 2016-2018 Runbox Solutions AS (runbox.com). +// +// This file is part of Runbox 7. +// +// Runbox 7 is free software: You can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// Runbox 7 is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Runbox 7. If not, see . +// ---------- END RUNBOX LICENSE ---------- + +import { XapianAPI } from './rmmxapianapi'; +import { MailAddressInfo } from './mailaddressinfo'; + +export class MessageInfo { + deletedFlag: boolean; + + constructor( + public id: number, + public changedDate: Date, + public messageDate: Date, + public folder: string, + public seenFlag: boolean, + public answeredFlag: boolean, + public flaggedFlag: boolean, + public from: MailAddressInfo[], + public to: MailAddressInfo[], + public cc: MailAddressInfo[], + public bcc: MailAddressInfo[], + public subject: string, + public plaintext: string, + public size: number, + public attachment: boolean) { + } + + static getSubjectWithoutAbbreviation(subject: string) { + const emailsubjectabbreviations = [ + 'RE:', + 'Re:', + 'FWD:', + 'Fwd:', + 'Fw:', + 'SV:', + 'VS:', + 'VB:' + ]; + const subjectparts = subject ? subject.split(' ') : []; + let subjectTextStart = 0; + while (emailsubjectabbreviations.find(abbr => abbr === subjectparts[subjectTextStart])) { + subjectTextStart ++; + } + return subjectparts.slice(subjectTextStart).join(' '); + } +} + +export class IndexingTools { + + constructor(private indexAPI: XapianAPI) { + + } + + public hashCode(str: string): number { + let hash = 0; + if (!str || str.length === 0) { + return hash; + } + + for (let i = 0; i < str.length; i++) { + const char = str.charCodeAt(i); + // tslint:disable-next-line:no-bitwise + hash = ((hash << 5) - hash) + char; + // tslint:disable-next-line:no-bitwise + hash = hash & hash; // Convert to 32bit integer + } + return hash; + } + + public markMessageSeen(messageId: number, seenFlag: boolean) { + try { + if (seenFlag === true) { + this.indexAPI.addTermToDocument(`Q${messageId}`, 'XFseen'); + } else if (seenFlag === false) { + this.indexAPI.removeTermFromDocument(`Q${messageId}`, 'XFseen'); + } + } catch (e) { + console.error(e); + } + } + + public flagMessage(messageId: number, flag: boolean) { + try { + if (flag === true) { + this.indexAPI.addTermToDocument(`Q${messageId}`, 'XFflagged'); + } else if (flag === false) { + this.indexAPI.removeTermFromDocument(`Q${messageId}`, 'XFflagged'); + } + } catch (e) { + console.error(e); + } + } + + public addMessageToIndex(msginfo: MessageInfo, + foldersNotToIndex?: string[] + ) { + if ( + foldersNotToIndex && foldersNotToIndex.find(foldername => + msginfo.folder === foldername) + ) { + this.indexAPI.deleteDocumentByUniqueTerm('Q' + msginfo.id); + console.log('Deleted msg id search index', msginfo.id); + return; + } + + let conversationId = msginfo.subject ? msginfo.subject.toUpperCase() : '0'; + + const beforeSubjectAbbreviationRemoval = conversationId; + + // Remove email subject abbreviation (Re fwd etc) + conversationId = MessageInfo.getSubjectWithoutAbbreviation(conversationId); + const afterSubjectAbbreviationRemoval: string = conversationId; + + let recipients = []; + + if (msginfo.to) { + recipients = recipients.concat(msginfo.to.map((mailAddr) => mailAddr.nameAndAddress.substr(0, 200))); + } + if (msginfo.cc) { + recipients = recipients.concat(msginfo.cc.map((mailAddr) => mailAddr.nameAndAddress.substr(0, 200))); + } + + // Remove non alphanumeric characters so that it's possible to use Xapian range processor + conversationId = conversationId.replace(/[^0-9A-Z]/g, ''); + + // Add last word hash to conversation id + const lastWordHash = msginfo.plaintext ? this.hashCode(msginfo.plaintext.split(/\s/) + .reverse() + .find((word) => word.replace(/[^a-z0-9]+/ig, '').trim().length >= 3)) : 0; + conversationId += '' + (0x100000000 + lastWordHash).toString(16).substr(1).toUpperCase(); + + const msgDateString = msginfo.messageDate.toJSON().substr(0, 'YYYY-MM-DD HH:mm'.length).replace(/[^0-9]/g, ''); + + const fromAddressInfo = msginfo.from && msginfo.from[0] ? msginfo.from[0] : new MailAddressInfo('', ''); + const visibleFrom = fromAddressInfo.name ? fromAddressInfo.name : fromAddressInfo.address ? fromAddressInfo.address : ''; + + // console.log(conversationId); + this.indexAPI.addSortableEmailToXapianIndex( + 'Q' + msginfo.id, + visibleFrom, + visibleFrom.toUpperCase(), + fromAddressInfo.address, + recipients, + msginfo.subject, + conversationId, + msgDateString, + msginfo.size, + msginfo.plaintext ? msginfo.plaintext : '', + msginfo.folder, + msginfo.seenFlag, + msginfo.flaggedFlag, + msginfo.answeredFlag, + msginfo.attachment + ); + } +} diff --git a/ts/xapian/rmmxapianapi.ts b/ts/xapian/rmmxapianapi.ts new file mode 100644 index 0000000..fa19968 --- /dev/null +++ b/ts/xapian/rmmxapianapi.ts @@ -0,0 +1,274 @@ +// --------- BEGIN RUNBOX LICENSE --------- +// Copyright (C) 2016-2018 Runbox Solutions AS (runbox.com). +// +// This file is part of Runbox 7. +// +// Runbox 7 is free software: You can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// Runbox 7 is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Runbox 7. If not, see . +// ---------- END RUNBOX LICENSE ---------- + +declare var Module; +declare var termlistresult; + +const emAllocateString = function (str) { + if (!str) { + str = ''; + } + + const $str = Module._malloc(str.length * 4 + 1); + Module.stringToUTF8(str, $str, str.length * 4 + 1); + return $str; +}; + +export class XapianAPI { + + public initXapianIndex: (path: string) => void = Module.cwrap('initXapianIndex', null, ['string']); + public initXapianIndexReadOnly: (path: string) => void = Module.cwrap('initXapianIndexReadOnly', null, ['string']); + public addSingleFileXapianIndex: (path: string) => void = Module.cwrap('addSingleFileXapianIndex', null, ['string']); + public addFolderXapianIndex: (path: string) => void = Module.cwrap('addFolderXapianIndex', null, ['string']); + public compactDatabase: () => void = Module.cwrap('compactDatabase', null, []); + public compactToWritableDatabase: (path: string) => void = Module.cwrap('compactToWritableDatabase', null, ['string']); + public addToXapianIndex: (id: string, val: string) => void = Module.cwrap('addToXapianIndex', null, ['string', 'string']); + public commitXapianUpdates: () => void = Module.cwrap('commitXapianUpdates', null, []); + public getXapianDocCount: () => number = Module.cwrap('getDocCount', 'number', []); + public getLastDocid: () => number = Module.cwrap('getLastDocid', 'number', []); + public reloadXapianDatabase: () => void = Module.cwrap('reloadDatabase', null, []); + public closeXapianDatabase: () => void = Module.cwrap('closeDatabase', null, []); + public setStringValueRange: (valuenumber: number, prefix: string) => + void = Module.cwrap('setStringValueRange', null, ['number', 'string']); + public clearValueRange: () => void = Module.cwrap('clearValueRange', null, []); + public getNumericValue: (docid: number, slot: number) => number = Module.cwrap('getNumericValue', 'number', ['number', 'number']); + public termlist: (prefix: string) => number = Module.cwrap('termlist', 'number', ['string']); + public documentTermList: (docid: number) => number = Module.cwrap('documentTermList', 'number', ['number']); + public documentXTermList: (docid: number) => number = Module.cwrap('documentXTermList', 'number', ['number']); + public deleteDocumentByUniqueTerm: (id: string) => void = Module.cwrap('deleteDocumentByUniqueTerm', null, ['string']); + public deleteDocumentFromAddedWritablesByUniqueTerm: (id: string) => number = + Module.cwrap('deleteDocumentFromAddedWritablesByUniqueTerm', 'number', ['string']); + public setStringValue: (docid: number, slot: number, val: string) => void = + Module.cwrap('setStringValue', null, ['number', 'number', 'string']); + public changeDocumentsFolder: (unique_term: string, folder: string) => + void = Module.cwrap('changeDocumentsFolder', null, ['string', 'string']); + public addTermToDocument: (idterm: string, termname: string) => void = Module.cwrap('addTermToDocument', null, ['string', 'string']); + public removeTermFromDocument: (idterm: string, termname: string) => void = + Module.cwrap('removeTermFromDocument', null, ['string', 'string']); + public addTextToDocument: (idterm: string, withoutpositions: boolean, text: string) => void = + Module.cwrap('addTextToDocument', null, ['string', 'boolean', 'string']); + public getDocIdFromUniqueIdTerm: (idterm: string) => number = + Module.cwrap('getDocIdFromUniqueIdTerm', 'number', ['string']); + + public getStringValue(docid, slot): string { + const $ret = Module._malloc(1024); + Module._getStringValue(docid, slot, $ret); + const ret = Module.UTF8ToString($ret); + Module._free($ret); + return ret; + } + + public listFolders(): any[] { + const $ret = Module._malloc(8192); + Module._listFolders($ret); + const cret = Module.UTF8ToString($ret); + Module._free($ret); + const ret: any[] = cret.split(','); + ret.forEach((r: string, ndx: number) => { + ret[ndx] = r.split(':'); + ret[ndx][1] = parseInt(ret[ndx][1], 10); + }); + return ret; + } + + public listUnreadFolders(): any[] { + const $ret = Module._malloc(8192); + const folderCount: number = Module._listUnreadFolders($ret); + if (folderCount > 0) { + const cret = Module.UTF8ToString($ret); + Module._free($ret); + const ret: any[] = cret.split(','); + ret.forEach((r: string, ndx: number) => { + ret[ndx] = r.split(':'); + ret[ndx][1] = parseInt(ret[ndx][1], 10); + }); + return ret; + } else { + return []; + } + } + + public queryXapianIndex(querystring, offset, maxresults): Array { + const $searchResults = Module._malloc(4 * maxresults); + Module.HEAP8.set(new Uint8Array(maxresults * 4), $searchResults); + + const $queryString = emAllocateString(querystring); + const $resultIdTerm = Module._malloc(128); + + const hits = Module._queryIndex($queryString, $searchResults, offset, maxresults); + // console.log(hits); + const results = new Array(hits); + for (let n = 0; n < hits; n++) { + const docid = Module.getValue($searchResults + (n * 4), 'i32'); + Module._getDocumentData(docid, $resultIdTerm); + results[n] = Module.UTF8ToString($resultIdTerm); + } + Module._free($searchResults); + Module._free($queryString); + Module._free($resultIdTerm); + return results; + + } + + public sortedXapianQuery(querystring: string, + sortcol: number, + reverse: number, + offset: number, + maxresults: number, + collapsecol: number): Array { + const $searchResults = Module._malloc(4 * maxresults); + const $collapseCount = Module._malloc(4 * maxresults); + + Module.HEAP8.set(new Uint8Array(maxresults * 4), $searchResults); + Module.HEAP8.set(new Uint8Array(maxresults * 4), $collapseCount); + + const $queryString = emAllocateString(querystring); + + const hits = Module._sortedXapianQuery($queryString, sortcol, reverse, $searchResults, offset, maxresults, collapsecol, $collapseCount); + // console.log("Sorted xapian query returned "+hits); + + const results = new Array(hits); + for (let n = 0; n < hits; n++) { + results[n] = [ + Module.getValue($searchResults + (n * 4), 'i32'), + Module.getValue($collapseCount + (n * 4), 'i32') + ]; + + } + Module._free($collapseCount); + Module._free($searchResults); + Module._free($queryString); + return results; + } + + public getDocumentData(docid) { + const $docdata = Module._malloc(1024); + Module._getDocumentData(docid, $docdata); + const ret = Module.UTF8ToString($docdata); + Module._free($docdata); + return ret; + } + + public addSortableEmailToXapianIndex( + idTerm, // Message id + sender, // from (name or email address if no name is present) + sortableFrom, // e.g. uppercase of the field above + fromEmailAddress, + recipients, // Array of strings with recipient email addresses + subject, + sortableSubject, // Uppercase of subject + dateString, // Datestring (YYYYMMDDHHmm) + messageSize, // mail message size in bytes + text, // mail text content (may also add attachment text here) + folder: string, + seen: boolean, // seen_flag (0 if unread, 1 if read) + flagged: boolean, // seen_flag (0 if unread, 1 if read) + answered: boolean, // seen_flag (0 if unread, 1 if read) + attachment: boolean // (0 if no attachments, 1 if message has attachments) + ) { + let $pointerPtr = 0; + + let pointers; + if (recipients) { + pointers = new Uint32Array(recipients.length); + for (let n = 0; n < recipients.length; n++) { + const recp = recipients[n]; + + const $recp = Module._malloc(recp.length * 4 + 1); + Module.stringToUTF8(recp, $recp, recp.length * 4 + 1); + pointers[n] = $recp; + } + + // Allocate bytes needed for the array of pointers + const nPointerBytes = pointers.length * pointers.BYTES_PER_ELEMENT; + $pointerPtr = Module._malloc(nPointerBytes); + + // Copy array of pointers to Emscripten heap + const pointerHeap = new Uint8Array(Module.HEAPU8.buffer, $pointerPtr, nPointerBytes); + pointerHeap.set(new Uint8Array(pointers.buffer)); + + } + + + const $idTerm = emAllocateString(idTerm); + const $sender = emAllocateString(sender); + const $sortableFrom = emAllocateString(sortableFrom); + const $fromEmailAddress = emAllocateString(fromEmailAddress); + const $subject = emAllocateString(subject); + const $sortableSubject = emAllocateString(sortableSubject); + const $dateString = emAllocateString(dateString); + const $text = emAllocateString(text); + const $folder = emAllocateString(folder); + + Module._addSortableEmailToXapianIndex( + $idTerm, + $sender, + $sortableFrom, + $fromEmailAddress, + recipients ? recipients.length : 0, + $pointerPtr, + $subject, + $sortableSubject, + $dateString, + messageSize, + $text, + $folder, + (seen ? 1 : 0) + + (flagged ? 2 : 0) + + (answered ? 4 : 0) + + (attachment ? 8 : 0) + ); + Module._free($folder); + Module._free($text); + Module._free($dateString); + Module._free($sortableSubject); + Module._free($subject); + Module._free($fromEmailAddress); + Module._free($sortableFrom); + Module._free($sender); + Module._free($idTerm); + + if (pointers) { + Module._free($pointerPtr); + + for (let n = pointers.length - 1; n >= 0; n--) { + Module._free(pointers[n]); + } + } + } + + hasMessageId(id: number): boolean { + termlistresult = []; + this.termlist('Q' + id); + return termlistresult.findIndex(t => t === '') > -1; + } +} + +export class SearchParams { + constructor( + public querystring: string, + public sortcol: number, + public reverse: number, + public offset: number, + public maxresults: number, + public collapsecol: number + ) { + + } +} diff --git a/tsconfig.json b/tsconfig.json index e944ac1..222bebf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,6 +3,7 @@ "outDir": "build", "target": "es5", "module": "commonjs", + "declaration":true, "sourceMap": true, "moduleResolution": "node", "experimentalDecorators": true, @@ -13,6 +14,7 @@ ] }, "files": [ + "ts/xapian/index.ts", "ts/test/test.ts" ] } From f6b336198c8ae207c6978899b08b862736fb04f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tadeusz=20So=C5=9Bnierz?= Date: Wed, 8 Jul 2020 11:58:49 +0200 Subject: [PATCH 3/3] Add a dedicated function for calculating folder counts --- package.json | 2 +- rmmxapianapi.cc | 53 ++++++++++++++++++++++++++++++++ ts/test/xapian.test.ts | 64 ++++++++++++++++++++++++++++----------- ts/xapian/rmmxapianapi.ts | 19 ++++++++++++ 4 files changed, 120 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 1e0bbbd..0de7aea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "runbox-searchindex", - "version": "0.1.0", + "version": "0.2.0", "scripts": { "build": "node compilermmxapianapi.js --xapiandir=xapian/xapian-core && tsc", "test": "mocha-typescript-watch -p tsconfig.json build/test/test.js", diff --git a/rmmxapianapi.cc b/rmmxapianapi.cc index 16177b8..68d8a69 100644 --- a/rmmxapianapi.cc +++ b/rmmxapianapi.cc @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -523,6 +524,58 @@ extern "C" { return numfolders; } + // returns a pair: [total, unread] in `results[]` + int EMSCRIPTEN_KEEPALIVE getFolderMessageCounts(const char *folderName, int results[]) { + if (!dbc) return 0; + + Xapian::QueryParser queryparser; + queryparser.set_database(dbc->db); + queryparser.add_boolean_prefix("folder", "XFOLDER:"); + queryparser.add_boolean_prefix("flag", "XF"); + + string queryString = "folder:\""; + queryString += folderName; + queryString += "\""; + + try { + { + Xapian::Enquire enquire(dbc->db); + Xapian::Query query = queryparser.parse_query( + queryString + " AND NOT flag:seen", + Xapian::QueryParser::FLAG_DEFAULT | Xapian::QueryParser::FLAG_PARTIAL + ); + + enquire.set_query(query); + Xapian::MSet mset = enquire.get_mset(0, UINT_MAX); + results[1] = mset.size(); + } + + { + Xapian::Enquire enquire(dbc->db); + Xapian::Query query = queryparser.parse_query( + queryString, + Xapian::QueryParser::FLAG_DEFAULT | Xapian::QueryParser::FLAG_PARTIAL + ); + + enquire.set_query(query); + Xapian::MSet mset = enquire.get_mset(0, UINT_MAX); + results[0] = mset.size(); + } + + return 1; + } catch(const Xapian::QueryParserError e) { + cout << "Invalid query: " << queryString << endl; + return 0; + } catch(const Xapian::Error e) { + cout << "Error: " << e.get_type() << " " + << e.get_msg() << " " + << e.get_error_string() << " " + << e.get_description() + << endl; + return 0; + } + } + int EMSCRIPTEN_KEEPALIVE sortedXapianQuery(char * searchtext, int sortvaluenum, bool reverse, int results[], diff --git a/ts/test/xapian.test.ts b/ts/test/xapian.test.ts index 024d7cd..d03133b 100644 --- a/ts/test/xapian.test.ts +++ b/ts/test/xapian.test.ts @@ -1,5 +1,5 @@ import { suite, test, timeout } from "@testdeck/mocha"; -import { equal } from 'assert'; +import { equal, notEqual, ok } from 'assert'; import { existsSync, mkdirSync } from 'fs'; import { execSync } from 'child_process'; @@ -16,6 +16,7 @@ declare var FS, MEMFS, NODEFS; const totalMessages = 20000; +const initialSeenMessages = 1234; const messages: MessageInfo[] = []; const messagesById: {[id: number]: MessageInfo} = {}; @@ -79,7 +80,7 @@ const contents = [ const msg = new MessageInfo(id, new Date(id * 6 * 60 * 60 * 1000), new Date(id * 6 * 60 * 60 * 1000), 'Inbox', - false, + (id <= initialSeenMessages), false, false, [new MailAddressInfo('Sender', 'sender@runbox.com')], @@ -184,6 +185,45 @@ const contents = [ }); } + @test() folderMessageCounters() { + const xapian = new XapianAPI(); + + const [fastTotal, fastUnread] = xapian.getFolderMessageCounts('Testfolder'); + const sortedUnread = xapian.sortedXapianQuery(`folder:"Testfolder" AND NOT flag:seen`, 0, 0, 0, 100000, -1).length; + const sortedTotal = xapian.sortedXapianQuery(`folder:"Testfolder"`, 0, 0, 0, 100000, -1).length; + + console.log(fastTotal, fastUnread, sortedTotal, sortedUnread); + + notEqual(sortedTotal, sortedUnread); // make sure this test makes sense + equal(fastUnread, sortedUnread); + equal(fastTotal, sortedTotal); + } + + @test(timeout(10000)) folderMessageCounterBenchmark() { + const xapian = new XapianAPI(); + const iterations = 1_000; + + console.log("Checking using sortedXapianQuery..."); + const timestamp = (): number => (new Date()).getTime(); + let t1 = timestamp(); + for (let i = 0; i < iterations; i++) { + xapian.sortedXapianQuery(`folder:"Testfolder" AND NOT flag:seen`, 0, 0, 0, 100000, -1).length; + xapian.sortedXapianQuery(`folder:"Testfolder"`, 0, 0, 0, 100000, -1).length; + } + + const sortedQueryTime = timestamp() - t1; + console.log(`sortedXapianQuery runtime: ${sortedQueryTime}ms (${sortedQueryTime / iterations}ms per query)`); + + console.log("Checking using getFolderMessageCounts..."); + t1 = timestamp(); + for (let i = 0; i < iterations; i++) { + xapian.getFolderMessageCounts('Testfolder'); + } + const getFolderMessageCountsTime = timestamp() - t1; + console.log(`getFolderMessageCounts runtime: ${getFolderMessageCountsTime}ms (${getFolderMessageCountsTime / iterations}ms per query)`); + ok(sortedQueryTime > (2 * getFolderMessageCountsTime), 'getFolderMessageCounts() is noticably faster than using sortedXapianQuery()'); + } + @test(timeout(10000)) moveMessages2() { const xapian = new XapianAPI(); @@ -330,8 +370,6 @@ const contents = [ results = xapian.sortedXapianQuery(`flag:flagged`, 0, 0, 0, 100000, -1); console.log('Number of flagged messages after flagging', results.length); equal(results.length, 1); - - } @test() answeredmessage() { @@ -349,27 +387,22 @@ const contents = [ results = xapian.sortedXapianQuery(`flag:answered`, 0, 0, 0, 100000, -1); console.log('Number of flagged messages after flagging answered', results.length); equal(results.length, 1); - - } @test() seenmessage() { const xapian = new XapianAPI(); - let results = xapian.sortedXapianQuery(`flag:seen`, 0, 0, 0, 100000, -1); - console.log('Number of flagged messages before seen flag', results.length); - equal(results.length, 0); + const seenBeforeChange = xapian.sortedXapianQuery(`flag:seen`, 0, 0, 0, 100000, -1).length; + console.log('Number of flagged messages before seen flag', seenBeforeChange); const indexer : IndexingTools = new IndexingTools(xapian); const flaggedMessage = messages[2]; flaggedMessage.seenFlag = true; indexer.addMessageToIndex(flaggedMessage); - results = xapian.sortedXapianQuery(`flag:seen`, 0, 0, 0, 100000, -1); - console.log('Number of flagged messages after seen', results.length); - equal(results.length, 1); - - + const seenAfterChange = xapian.sortedXapianQuery(`flag:seen`, 0, 0, 0, 100000, -1).length; + console.log('Number of flagged messages after seen', seenAfterChange); + equal(seenAfterChange, seenBeforeChange + 1); } @test() messagewithattachment() { @@ -387,8 +420,5 @@ const contents = [ results = xapian.sortedXapianQuery(`flag:attachment`, 0, 0, 0, 100000, -1); console.log('Number of attachment messages after attachment flag', results.length); equal(results.length, 1); - - } - } diff --git a/ts/xapian/rmmxapianapi.ts b/ts/xapian/rmmxapianapi.ts index fa19968..55a57c2 100644 --- a/ts/xapian/rmmxapianapi.ts +++ b/ts/xapian/rmmxapianapi.ts @@ -126,6 +126,25 @@ export class XapianAPI { } + public getFolderMessageCounts(folder: string): number[] { + const $folderString = emAllocateString(folder); + const $results = Module._malloc(4 * 2); + Module.HEAP8.set(new Uint8Array(4 * 2), $results); + + const ret = Module._getFolderMessageCounts($folderString, $results); + let results: number[]; + + if (ret !== 0) { + results = new Array(2); + results[0] = Module.getValue($results, 'i32'); + results[1] = Module.getValue($results + 4, 'i32'); + } + + Module._free($folderString); + Module._free($results); + return results; + } + public sortedXapianQuery(querystring: string, sortcol: number, reverse: number,