From b0bec65c3b31058eeb8e86980c22340763e90add Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Thu, 8 Mar 2018 17:14:19 -0500 Subject: [PATCH 01/23] Migrate to Typescript --- .gitignore | 4 +- .jshintrc | 19 - .travis.yml | 18 +- bower.json | 42 - build/footer | 7 - gulpfile.js | 115 - package-lock.json | 4491 +++++++++++++++++++ package.json | 44 +- src/firebase/geoCallbackRegistration.ts | 27 + src/firebase/geoFire.ts | 148 + src/firebase/geoFireUtils.ts | 437 ++ src/firebase/geoQuery.ts | 527 +++ src/geoCallbackRegistration.js | 31 - src/geoFire.js | 142 - src/geoFireUtils.js | 468 -- src/geoQuery.js | 560 --- build/header => src/index.ts | 3 +- src/interfaces/geoFireObj.ts | 5 + src/interfaces/index.ts | 2 + src/interfaces/queryCriteria.ts | 4 + tests/common.ts | 128 + tests/geoCallbackRegistration.test.ts | 253 ++ tests/geoFire.test.ts | 842 ++++ tests/geoFireUtils.test.ts | 296 ++ tests/geoQuery.test.ts | 1412 ++++++ tests/index.html | 34 - tests/karma.conf.js | 24 - tests/specs/common.spec.js | 125 - tests/specs/geoCallbackRegistration.spec.js | 242 - tests/specs/geoFire.spec.js | 829 ---- tests/specs/geoFireUtils.spec.js | 269 -- tests/specs/geoQuery.spec.js | 1404 ------ tsconfig.json | 22 + 33 files changed, 8625 insertions(+), 4349 deletions(-) delete mode 100644 .jshintrc delete mode 100644 bower.json delete mode 100644 build/footer delete mode 100644 gulpfile.js create mode 100644 package-lock.json create mode 100644 src/firebase/geoCallbackRegistration.ts create mode 100644 src/firebase/geoFire.ts create mode 100644 src/firebase/geoFireUtils.ts create mode 100644 src/firebase/geoQuery.ts delete mode 100644 src/geoCallbackRegistration.js delete mode 100644 src/geoFire.js delete mode 100644 src/geoFireUtils.js delete mode 100644 src/geoQuery.js rename build/header => src/index.ts (90%) create mode 100644 src/interfaces/geoFireObj.ts create mode 100644 src/interfaces/index.ts create mode 100644 src/interfaces/queryCriteria.ts create mode 100644 tests/common.ts create mode 100644 tests/geoCallbackRegistration.test.ts create mode 100755 tests/geoFire.test.ts create mode 100644 tests/geoFireUtils.test.ts create mode 100644 tests/geoQuery.test.ts delete mode 100755 tests/index.html delete mode 100644 tests/karma.conf.js delete mode 100644 tests/specs/common.spec.js delete mode 100644 tests/specs/geoCallbackRegistration.spec.js delete mode 100755 tests/specs/geoFire.spec.js delete mode 100644 tests/specs/geoFireUtils.spec.js delete mode 100644 tests/specs/geoQuery.spec.js create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 228345a4..8901d41b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ dist/ node_modules/ -bower_components/ -tests/coverage/ \ No newline at end of file +tests/coverage/ +.DS_Store \ No newline at end of file diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 545892b3..00000000 --- a/.jshintrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "predef": [ - ], - "bitwise": true, - "curly": true, - "eqeqeq": true, - "forin": true, - "freeze": true, - "indent": 2, - "latedef": false, - "node": true, - "noempty": true, - "nonbsp": true, - "quotmark": "double", - "strict": true, - "trailing": true, - "undef": true, - "unused": true -} diff --git a/.travis.yml b/.travis.yml index 33db554e..9159680a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,14 @@ language: node_js node_js: -- '0.12' -#- stable + - '0.12' + #- stable sudo: false before_install: -- export DISPLAY=:99.0 -- sh -e /etc/init.d/xvfb start + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start install: -- npm install -g bower -- npm install -- bower install + - npm install script: -- npm run travis -after_script: -- cat ./tests/coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js + - npm run test +after_success: + - npm run build diff --git a/bower.json b/bower.json deleted file mode 100644 index 08ff6227..00000000 --- a/bower.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "geofire", - "description": "Location-based querying and filtering using Firebase", - "version": "4.1.2", - "authors": [ - "Firebase (https://firebase.google.com/)" - ], - "homepage": "https://github.com/firebase/geofire-js/", - "repository": { - "type": "git", - "url": "https://github.com/firebase/geofire-js.git" - }, - "license": "MIT", - "keywords": [ - "geoquery", - "location", - "firebase", - "realtime", - "geolocation" - ], - "main": "dist/geofire.js", - "ignore": [ - "**/.*", - "src", - "build", - "tests", - "examples", - "node_modules", - "bower_components", - "firebase.json", - "package.json", - "gulpfile.js", - "changelog.txt" - ], - "dependencies": { - "firebase": "^2.4.0 || 3.x.x" - }, - "devDependencies": { - "jasmine": "~2.0.0", - "rsvp": "^3.1.0" - } -} diff --git a/build/footer b/build/footer deleted file mode 100644 index 7026eef9..00000000 --- a/build/footer +++ /dev/null @@ -1,7 +0,0 @@ - return GeoFire; -})(); - -// Export GeoFire if this is being run in node -if (typeof module !== "undefined" && typeof process !== "undefined") { - module.exports = GeoFire; -} \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index 4f3e6604..00000000 --- a/gulpfile.js +++ /dev/null @@ -1,115 +0,0 @@ -/**************/ -/* REQUIRES */ -/**************/ -var gulp = require("gulp"); - -// File IO -var streamqueue = require("streamqueue"); -var concat = require("gulp-concat"); -var jshint = require("gulp-jshint"); -var uglify = require("gulp-uglify"); -var runSequence = require('run-sequence'); - -// Testing -var karma = require("gulp-karma"); - - -/****************/ -/* FILE PATHS */ -/****************/ -var paths = { - destDir: "dist", - - scripts: { - src: { - dir: "src", - files: [ - "src/*.js" - ] - }, - dest: { - dir: "dist", - files: { - unminified: "geofire.js", - minified: "geofire.min.js" - } - } - }, - - tests: { - config: "tests/karma.conf.js", - files: [ - "bower_components/firebase/firebase.js", - "bower_components/rsvp/rsvp.min.js", - "src/*.js", - "tests/specs/*.spec.js" - ] - } -}; - - -/***********/ -/* TASKS */ -/***********/ -/* Lints, minifies, and concatenates the script files */ -gulp.task("scripts", function() { - // Concatenate all src files together - var stream = streamqueue({ objectMode: true }); - stream.queue(gulp.src("build/header")); - stream.queue(gulp.src(paths.scripts.src.files)); - stream.queue(gulp.src("build/footer")); - - // Output the final concatenated script file - return stream.done() - // Rename file - .pipe(concat(paths.scripts.dest.files.unminified)) - - // Lint - .pipe(jshint()) - .pipe(jshint.reporter("jshint-stylish")) - .pipe(jshint.reporter("fail")) - .on("error", function(error) { - throw error; - }) - - // Write un-minified version - .pipe(gulp.dest(paths.scripts.dest.dir)) - - // Minify - .pipe(uglify({ - preserveComments: "some" - })) - - // Rename file - .pipe(concat(paths.scripts.dest.files.minified)) - - // Write minified version to the distribution directory - .pipe(gulp.dest(paths.scripts.dest.dir)); -}); - -/* Uses the Karma test runner to run the Jasmine tests */ -gulp.task("test", function() { - return gulp.src(paths.tests.files) - .pipe(karma({ - configFile: paths.tests.config, - action: "run" - })) - .on("error", function(error) { - throw error; - }); -}); - -/* Re-runs the "scripts" task every time a script file changes */ -gulp.task("watch", function() { - gulp.watch(["build/*", paths.scripts.src.dir + "/**/*"], ["scripts"]); -}); - -/* Builds the distribution files */ -gulp.task("build", ["scripts"]); - -/* Runs the "test" and "scripts" tasks by default */ -gulp.task("default", function(done) { - runSequence("scripts", "test", function(error) { - done(error && error.err); - }); -}); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..56541918 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4491 @@ +{ + "name": "geofire", + "version": "4.1.2", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@firebase/app": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.1.10.tgz", + "integrity": "sha512-2GTXt3b2QZXkmx6/5nNJq+pEN/VTjAG55MFJS1WMoLVZkwKuNpWNk65QVyPaoL88x1iHtuLqAMFgJUOnhOg+Pw==", + "dev": true, + "requires": { + "@firebase/app-types": "0.1.2", + "@firebase/util": "0.1.10", + "tslib": "1.9.0" + } + }, + "@firebase/app-types": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.1.2.tgz", + "integrity": "sha512-bCIZGeMtP0ibrXNNaU214/1tRNw0jHnir/cfiAao1gjUyIS7RzOTQoH+zbwPJNEwUqJ0T3ykw/Tv4/khGqbVBg==", + "dev": true + }, + "@firebase/auth": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.3.4.tgz", + "integrity": "sha512-lpKpPGVyNEuQasukVgxrti/GptEZDE24B/UnRmvjiwpVlOpVPLsaNJkklLiODlH7DS3yIyGHWYqojNl3iaTEmA==", + "dev": true, + "requires": { + "@firebase/auth-types": "0.1.2" + } + }, + "@firebase/auth-types": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.1.2.tgz", + "integrity": "sha512-pofZTXrz/urWmH+5opF4jpuv6GEaWOQtX9dl4AKAjOYoLceRyJn4OEeZodsDYdp6kLyARH1mcYtFMyZ9jvUtYg==", + "dev": true + }, + "@firebase/database": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.1.11.tgz", + "integrity": "sha512-1TX8YlL3BCjgsbe1qvgg/r0SxOvIlJdmaktXXm6fnaPCjSD0Vhm5ln2EX3xGY97ft/Lruy9AA/7TfnAKIYA/+w==", + "dev": true, + "requires": { + "@firebase/database-types": "0.1.2", + "@firebase/util": "0.1.10", + "faye-websocket": "0.11.1", + "tslib": "1.9.0" + } + }, + "@firebase/database-types": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.1.2.tgz", + "integrity": "sha512-WDqyclInvbD6qNtbVlatzXW6SpsV8V1Nz7DaTM8z27CwxRtXakAXxERPnGipA0ZbZV4v+Xs1+6wC7Xc6T4EVaw==", + "dev": true + }, + "@firebase/firestore": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-0.3.4.tgz", + "integrity": "sha512-dASy7mtwXmV1beFmMciuInqkvICV5ImwvnDwt/5tMuQvqSwuCZwPEkU0Tkrht92PgnTkuhu/nnZbI624Li/VXA==", + "dev": true, + "requires": { + "@firebase/firestore-types": "0.2.2", + "@firebase/webchannel-wrapper": "0.2.6", + "grpc": "1.9.1", + "tslib": "1.9.0" + } + }, + "@firebase/firestore-types": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-0.2.2.tgz", + "integrity": "sha512-yuC07Zi8p0myCQoU62O0fnGcNEcWZnKEGcQ1tj71Qh3E3Dw7qPJ75kXeeL95Bh1PHI0+TqAcDTEb9yVG9xIUVw==", + "dev": true + }, + "@firebase/messaging": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.2.1.tgz", + "integrity": "sha512-r0kPCLvck5tqt1fygUxtjB/HiMv4uNcilfUmHqUOHhZT3G5NOSe7u3wVhc286jI70QyD/uJ0PsF3NzjdRA8v3g==", + "dev": true, + "requires": { + "@firebase/messaging-types": "0.1.2", + "@firebase/util": "0.1.10", + "lcov-result-merger": "2.0.0", + "tslib": "1.9.0" + } + }, + "@firebase/messaging-types": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@firebase/messaging-types/-/messaging-types-0.1.2.tgz", + "integrity": "sha512-4Oycm2JiDaLp9jUy4O25gD/B9Hqdy11hGjSNE0rzhVox5d0e1RF08QCwVt9xpjtBLRgEpPLyD9dPeSu4YK0Y4Q==", + "dev": true + }, + "@firebase/polyfill": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.1.6.tgz", + "integrity": "sha512-O58NFa/x+8peu6zZUAFKAZildlOTStIPtDClPKOeX8OIS20OH+JbMalTZ6pKqUqRbGGA52/6Ta8epgvN99+HRA==", + "dev": true, + "requires": { + "promise-polyfill": "7.1.0", + "tslib": "1.9.0" + } + }, + "@firebase/storage": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.1.8.tgz", + "integrity": "sha512-g0xYwJbgOuAaAJy5iAoEymS77m3oVqFh9IAF3A4LvqOC9q3v3ubSSYjpNHRPZstO68pMDKsNrqb2TcJgx92kSA==", + "dev": true, + "requires": { + "@firebase/storage-types": "0.1.2", + "tslib": "1.9.0" + } + }, + "@firebase/storage-types": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.1.2.tgz", + "integrity": "sha512-/nL93m2lIqzx4FajVnskn2YTDEj0ym53LCZegZpAPxm4GIkOQ8UhzzfHFfHJJCygb58xRszDkDuRlpJlakO4pA==", + "dev": true + }, + "@firebase/util": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.1.10.tgz", + "integrity": "sha512-XEogRfUQBZ4T37TMq/3ZbuiTdRAKX8hF3TgJglUZNCJf/6QnQ+jlupCuMAXBqCGfw2Mw0m2matoCUBWpsyevOA==", + "dev": true, + "requires": { + "tslib": "1.9.0" + } + }, + "@firebase/webchannel-wrapper": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.2.6.tgz", + "integrity": "sha512-Uv9ieuHVogIOOzpGmdjV3/0asMJPdssq2vrOYJ/UTlvekT6aGdv+sx2WWvIrGRWfFxWIkOxCqpqaGMYbhc88Pg==", + "dev": true + }, + "@types/chai": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.2.tgz", + "integrity": "sha512-D8uQwKYUw2KESkorZ27ykzXgvkDJYXVEihGklgfp5I4HUP8D6IxtcdLTMB1emjQiWzV7WZ5ihm1cxIzVwjoleQ==", + "dev": true + }, + "@types/mocha": { + "version": "2.2.48", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.48.tgz", + "integrity": "sha512-nlK/iyETgafGli8Zh9zJVCTicvU3iajSkRwOh3Hhiva598CMqNJ4NcVCGMTGKpGpTYj/9R8RLzS9NAykSSCqGw==", + "dev": true + }, + "@types/node": { + "version": "9.4.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.4.6.tgz", + "integrity": "sha512-CTUtLb6WqCCgp6P59QintjHWqzf4VL1uPA27bipLAPxFqrtK1gEYllePzTICGqQ8rYsCbpnsNypXjjDzGAAjEQ==", + "dev": true + }, + "JSONStream": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.2.tgz", + "integrity": "sha1-wQI3G27Dp887hHygDCC7D85Mbeo=", + "dev": true, + "requires": { + "jsonparse": "1.3.1", + "through": "2.3.8" + } + }, + "abab": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", + "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=", + "dev": true + }, + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "dev": true + }, + "acorn-globals": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz", + "integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==", + "dev": true, + "requires": { + "acorn": "5.5.3" + }, + "dependencies": { + "acorn": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", + "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", + "dev": true + } + } + }, + "acorn-node": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.3.0.tgz", + "integrity": "sha512-efP54n3d1aLfjL2UMdaXa6DsswwzJeI5rqhbFvXMrKiJ6eJFpf+7R0zN7t8IC+XKn2YOAFAv6xbBNgHUkoHWLw==", + "dev": true, + "requires": { + "acorn": "5.5.3", + "xtend": "4.0.1" + }, + "dependencies": { + "acorn": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", + "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", + "dev": true + } + } + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "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.3" + } + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "1.1.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", + "dev": true + }, + "array-filter": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", + "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", + "dev": true + }, + "array-map": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", + "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", + "dev": true + }, + "array-reduce": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", + "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "ascli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", + "integrity": "sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=", + "dev": true, + "requires": { + "colour": "0.7.1", + "optjs": "3.2.2" + } + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "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 + }, + "astw": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz", + "integrity": "sha1-e9QXhNMkk5h66yOba04cV6hzuRc=", + "dev": true, + "requires": { + "acorn": "4.0.13" + } + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "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 + }, + "base64-js": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.3.tgz", + "integrity": "sha512-MsAhsUW1GxCdgYSO6tAfZrNapmUKk7mWx/k5mFY/A1gBtkaCaNapTg+FExCw1r9yeaZhqx/xPg43xgTFH6KL5w==", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "boom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "dev": true, + "requires": { + "hoek": "4.2.1" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-pack": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.4.tgz", + "integrity": "sha512-Q4Rvn7P6ObyWfc4stqLWHtG1MJ8vVtjgT24Zbu+8UTzxYuZouqZsmNRRTFVMY/Ux0eIKv1d+JWzsInTX+fdHPQ==", + "dev": true, + "requires": { + "JSONStream": "1.3.2", + "combine-source-map": "0.8.0", + "defined": "1.0.0", + "safe-buffer": "5.1.1", + "through2": "2.0.3", + "umd": "3.0.1" + } + }, + "browser-process-hrtime": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz", + "integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44=", + "dev": true + }, + "browser-resolve": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", + "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=", + "dev": true, + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "browserify": { + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.1.1.tgz", + "integrity": "sha512-iSH21jK0+IApV8YHOfmGt1qsGd74oflQ1Ko/28JOkWLFNBngAQfKb6WYIJ9CufH8vycqKX1sYU3y7ZrVhwevAg==", + "dev": true, + "requires": { + "JSONStream": "1.3.2", + "assert": "1.4.1", + "browser-pack": "6.0.4", + "browser-resolve": "1.11.2", + "browserify-zlib": "0.2.0", + "buffer": "5.1.0", + "cached-path-relative": "1.0.1", + "concat-stream": "1.6.1", + "console-browserify": "1.1.0", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.12.0", + "defined": "1.0.0", + "deps-sort": "2.0.0", + "domain-browser": "1.2.0", + "duplexer2": "0.1.4", + "events": "2.0.0", + "glob": "7.1.2", + "has": "1.0.1", + "htmlescape": "1.1.1", + "https-browserify": "1.0.0", + "inherits": "2.0.3", + "insert-module-globals": "7.0.2", + "labeled-stream-splicer": "2.0.0", + "mkdirp": "0.5.1", + "module-deps": "6.0.0", + "os-browserify": "0.3.0", + "parents": "1.0.1", + "path-browserify": "0.0.0", + "process": "0.11.10", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "read-only-stream": "2.0.0", + "readable-stream": "2.3.5", + "resolve": "1.5.0", + "shasum": "1.0.2", + "shell-quote": "1.6.1", + "stream-browserify": "2.0.1", + "stream-http": "2.8.0", + "string_decoder": "1.0.3", + "subarg": "1.0.0", + "syntax-error": "1.4.0", + "through2": "2.0.3", + "timers-browserify": "1.4.2", + "tty-browserify": "0.0.1", + "url": "0.11.0", + "util": "0.10.3", + "vm-browserify": "0.0.4", + "xtend": "4.0.1" + } + }, + "browserify-aes": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz", + "integrity": "sha512-UGnTYAnB2a3YuYKIRy1/4FB2HdM866E0qC46JXvVTYKlBlZlnvfpSfY6OKfXZAkv70eJ2a1SqzpAo5CRhZGDFg==", + "dev": true, + "requires": { + "buffer-xor": "1.0.3", + "cipher-base": "1.0.4", + "create-hash": "1.1.3", + "evp_bytestokey": "1.0.3", + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + }, + "browserify-cipher": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", + "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", + "dev": true, + "requires": { + "browserify-aes": "1.1.1", + "browserify-des": "1.0.0", + "evp_bytestokey": "1.0.3" + } + }, + "browserify-des": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", + "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", + "dev": true, + "requires": { + "cipher-base": "1.0.4", + "des.js": "1.0.0", + "inherits": "2.0.3" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "randombytes": "2.0.6" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.1.3", + "create-hmac": "1.1.6", + "elliptic": "6.4.0", + "inherits": "2.0.3", + "parse-asn1": "5.1.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "1.0.6" + } + }, + "buffer": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.1.0.tgz", + "integrity": "sha512-YkIRgwsZwJWTnyQrsBTWefizHh+8GYj3kbL1BTiAQ/9pwpino0G7B2gp5tx/FUBqUlvtxV85KNR3mwfAtv15Yw==", + "dev": true, + "requires": { + "base64-js": "1.2.3", + "ieee754": "1.1.8" + } + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "bytebuffer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", + "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=", + "dev": true, + "requires": { + "long": "3.2.0" + } + }, + "cached-path-relative": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz", + "integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc=", + "dev": true + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chai": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", + "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", + "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.8" + } + }, + "chalk": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", + "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.3.0" + }, + "dependencies": { + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz", + "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" + } + }, + "clone": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", + "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", + "dev": true + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "cloneable-readable": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.1.tgz", + "integrity": "sha512-DNNEq6JdqBFPzS29TaoqZFPNLn5Xn3XyPFqLIhyBT8Xou4lHQEWzD6FinXoJUfhIfWX3aE1JkRa3cbWCHFbt1g==", + "dev": true, + "requires": { + "inherits": "2.0.3", + "process-nextick-args": "2.0.0", + "readable-stream": "2.3.5" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "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 + }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colour": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz", + "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g=", + "dev": true + }, + "combine-source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", + "dev": true, + "requires": { + "convert-source-map": "1.1.3", + "inline-source-map": "0.6.2", + "lodash.memoize": "3.0.4", + "source-map": "0.5.7" + } + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "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 + }, + "concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-gslSSJx03QKa59cIKqeJO9HQ/WZMotvYJCuaUULrLpjj8oG40kV2Z+gz82pVxlTkOADi4PJxQPPfhl1ELYrrXw==", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5", + "typedarray": "0.0.6" + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "0.1.4" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-type-parser": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/content-type-parser/-/content-type-parser-1.0.2.tgz", + "integrity": "sha512-lM4l4CnMEwOLHAHr/P6MEZwZFPJFtAAKgL6pogbXmVZggIqXhdB6RbBtPOTsw2FcXwYhehRGERJmRrjOiIB8pQ==", + "dev": true + }, + "convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-ecdh": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", + "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "elliptic": "6.4.0" + } + }, + "create-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", + "dev": true, + "requires": { + "cipher-base": "1.0.4", + "inherits": "2.0.3", + "ripemd160": "2.0.1", + "sha.js": "2.4.10" + } + }, + "create-hmac": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", + "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", + "dev": true, + "requires": { + "cipher-base": "1.0.4", + "create-hash": "1.1.3", + "inherits": "2.0.3", + "ripemd160": "2.0.1", + "safe-buffer": "5.1.1", + "sha.js": "2.4.10" + } + }, + "cryptiles": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "dev": true, + "requires": { + "boom": "5.2.0" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "dev": true, + "requires": { + "hoek": "4.2.1" + } + } + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "1.0.0", + "browserify-sign": "4.0.4", + "create-ecdh": "4.0.0", + "create-hash": "1.1.3", + "create-hmac": "1.1.6", + "diffie-hellman": "5.0.2", + "inherits": "2.0.3", + "pbkdf2": "3.0.14", + "public-encrypt": "4.0.0", + "randombytes": "2.0.6", + "randomfill": "1.0.4" + } + }, + "cssom": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz", + "integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs=", + "dev": true + }, + "cssstyle": { + "version": "0.2.37", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz", + "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=", + "dev": true, + "requires": { + "cssom": "0.3.2" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "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.8" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "deps-sort": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", + "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=", + "dev": true, + "requires": { + "JSONStream": "1.3.2", + "shasum": "1.0.2", + "subarg": "1.0.0", + "through2": "2.0.3" + } + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0" + } + }, + "detective": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.1.0.tgz", + "integrity": "sha512-TFHMqfOvxlgrfVzTEkNBSh9SvSNX/HfF4OFI2QFGCyPm02EsyILqnUeb5P6q7JZ3SFNTBL5t2sePRgrN4epUWQ==", + "dev": true, + "requires": { + "acorn-node": "1.3.0", + "defined": "1.0.0", + "minimist": "1.2.0" + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", + "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "miller-rabin": "4.0.1", + "randombytes": "2.0.6" + } + }, + "dom-storage": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dom-storage/-/dom-storage-2.0.2.tgz", + "integrity": "sha1-7RfL9oq9EOCu+BgnE+KXxeS1ALA=", + "dev": true + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "dev": true, + "requires": { + "webidl-conversions": "4.0.2" + } + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "requires": { + "readable-stream": "2.3.5" + } + }, + "duplexify": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.4.tgz", + "integrity": "sha512-JzYSLYMhoVVBe8+mbHQ4KgpvHpm0DZpJuL8PY93Vyv1fW7jYJ90LoXa1di/CVbJM+TgMs91rbDapE/RNIfnJsA==", + "dev": true, + "requires": { + "end-of-stream": "1.4.1", + "inherits": "2.0.3", + "readable-stream": "2.3.5", + "stream-shift": "1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "elliptic": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "brorand": "1.1.0", + "hash.js": "1.1.3", + "hmac-drbg": "1.0.1", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0", + "minimalistic-crypto-utils": "1.0.1" + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "1.4.0" + } + }, + "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 + }, + "escodegen": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz", + "integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==", + "dev": true, + "requires": { + "esprima": "3.1.3", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "optionator": "0.8.2", + "source-map": "0.6.1" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "events": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-2.0.0.tgz", + "integrity": "sha512-r/M5YkNg9zwI8QbSf7tsDWWJvO3PGwZXyG7GpFAxtMASnHL2eblFd7iHiGPtyGKKFPZ59S63NeX10Ws6WqGDcg==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "1.3.4", + "safe-buffer": "5.1.1" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "0.1.1" + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "2.2.3" + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "faye-websocket": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", + "dev": true, + "requires": { + "websocket-driver": "0.7.0" + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fill-range": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "dev": true, + "requires": { + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.7", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" + } + }, + "firebase": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-4.10.1.tgz", + "integrity": "sha512-h3rzfitKYBQAIxvj5r41W3zP9B0y3WcDLk3kHE+fW+6Mxo2oDW9qSqWNVOjV1yqiGR/6tXKxJshQzqrbwpU3VA==", + "dev": true, + "requires": { + "@firebase/app": "0.1.10", + "@firebase/auth": "0.3.4", + "@firebase/database": "0.1.11", + "@firebase/firestore": "0.3.4", + "@firebase/messaging": "0.2.1", + "@firebase/polyfill": "0.1.6", + "@firebase/storage": "0.1.8", + "dom-storage": "2.0.2", + "xmlhttprequest": "1.8.0" + } + }, + "first-chunk-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", + "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "1.0.2" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.6", + "mime-types": "2.1.18" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": 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-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 + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "2.0.0", + "is-glob": "2.0.1" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "2.0.1" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "3.1.0", + "path-dirname": "1.0.2" + } + }, + "glob-stream": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", + "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", + "dev": true, + "requires": { + "extend": "3.0.1", + "glob": "5.0.15", + "glob-parent": "3.1.0", + "micromatch": "2.3.11", + "ordered-read-streams": "0.3.0", + "through2": "0.6.5", + "to-absolute-glob": "0.1.1", + "unique-stream": "2.2.1" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + } + } + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "dev": true + }, + "grpc": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.9.1.tgz", + "integrity": "sha512-WNW3MWMuAoo63AwIlzFE3T0KzzvNBSvOkg67Hm8WhvHNkXFBlIk1QyJRE3Ocm0O5eIwS7JU8Ssota53QR1zllg==", + "dev": true, + "requires": { + "lodash": "4.17.5", + "nan": "2.9.2", + "node-pre-gyp": "0.6.39", + "protobufjs": "5.0.2" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "ajv": { + "version": "4.11.8", + "bundled": true, + "dev": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.3" + } + }, + "asn1": { + "version": "0.2.3", + "bundled": true, + "dev": true + }, + "assert-plus": { + "version": "0.2.0", + "bundled": true, + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "dev": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true, + "dev": true + }, + "aws4": { + "version": "1.6.0", + "bundled": true, + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "boom": { + "version": "2.10.1", + "bundled": true, + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.8", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "caseless": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "co": { + "version": "4.6.0", + "bundled": true, + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "dev": true, + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true + } + } + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "bundled": true, + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "extend": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "extsprintf": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "dev": true + }, + "form-data": { + "version": "2.1.4", + "bundled": true, + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true, + "dev": true + }, + "har-schema": { + "version": "1.0.5", + "bundled": true, + "dev": true + }, + "har-validator": { + "version": "4.2.1", + "bundled": true, + "dev": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "dev": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "bundled": true, + "dev": true + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "jsonify": { + "version": "0.0.0", + "bundled": true, + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true + } + } + }, + "mime-db": { + "version": "1.30.0", + "bundled": true, + "dev": true + }, + "mime-types": { + "version": "2.1.17", + "bundled": true, + "dev": true, + "requires": { + "mime-db": "1.30.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "node-pre-gyp": { + "version": "0.6.39", + "bundled": true, + "dev": true, + "requires": { + "detect-libc": "1.0.3", + "hawk": "3.1.3", + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.2", + "rc": "1.2.4", + "request": "2.81.0", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "2.2.1", + "tar-pack": "3.4.1" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.4" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "osenv": { + "version": "0.1.4", + "bundled": true, + "dev": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "performance-now": { + "version": "0.2.0", + "bundled": true, + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true, + "dev": true + }, + "punycode": { + "version": "1.4.1", + "bundled": true, + "dev": true + }, + "qs": { + "version": "6.4.0", + "bundled": true, + "dev": true + }, + "rc": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true + } + } + }, + "readable-stream": { + "version": "2.3.3", + "bundled": true, + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "request": { + "version": "2.81.0", + "bundled": true, + "dev": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.2.1" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "sshpk": { + "version": "1.13.1", + "bundled": true, + "dev": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true + } + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "stringstream": { + "version": "0.0.5", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "dev": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-pack": { + "version": "3.4.1", + "bundled": true, + "dev": true, + "requires": { + "debug": "2.6.9", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.3.3", + "rimraf": "2.6.2", + "tar": "2.2.1", + "uid-number": "0.0.6" + } + }, + "tough-cookie": { + "version": "2.3.3", + "bundled": true, + "dev": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "dev": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true, + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "uuid": { + "version": "3.2.1", + "bundled": true, + "dev": true + }, + "verror": { + "version": "1.10.0", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true + } + } + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + } + } + }, + "gulp-sourcemaps": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz", + "integrity": "sha1-uG/zSdgBzrVuHZ59x7vLS33uYAw=", + "dev": true, + "requires": { + "convert-source-map": "1.1.3", + "graceful-fs": "4.1.11", + "strip-bom": "2.0.0", + "through2": "2.0.3", + "vinyl": "1.2.0" + }, + "dependencies": { + "clone": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", + "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=", + "dev": true + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "dev": true, + "requires": { + "clone": "1.0.3", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "dev": true, + "requires": { + "ajv": "5.5.2", + "har-schema": "2.0.0" + } + }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "dev": true, + "requires": { + "function-bind": "1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "hash-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "dev": true, + "requires": { + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0" + } + }, + "hawk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "dev": true, + "requires": { + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.1", + "sntp": "2.1.0" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "1.1.3", + "minimalistic-assert": "1.0.0", + "minimalistic-crypto-utils": "1.0.1" + } + }, + "hoek": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", + "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", + "dev": true + }, + "html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "dev": true, + "requires": { + "whatwg-encoding": "1.0.3" + } + }, + "htmlescape": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", + "dev": true + }, + "http-parser-js": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.11.tgz", + "integrity": "sha512-QCR5O2AjjMW8Mo4HyI1ctFcv+O99j/0g367V3YoVnrNw5hkDvAWZD0lWGcc+F4yN3V55USPCVix4efb75HxFfA==", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "dev": true + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "dev": true, + "requires": { + "source-map": "0.5.7" + } + }, + "insert-module-globals": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.2.tgz", + "integrity": "sha512-p3s7g96Nm62MbHRuj9ZXab0DuJNWD7qcmdUXCOQ/ZZn42DtDXfsLill7bq19lDCx3K3StypqUnuE3H2VmIJFUw==", + "dev": true, + "requires": { + "JSONStream": "1.3.2", + "combine-source-map": "0.7.2", + "concat-stream": "1.5.2", + "is-buffer": "1.1.6", + "lexical-scope": "1.2.0", + "process": "0.11.10", + "through2": "2.0.3", + "xtend": "4.0.1" + }, + "dependencies": { + "combine-source-map": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.7.2.tgz", + "integrity": "sha1-CHAxKFazB6h8xKxIbzqaYq7MwJ4=", + "dev": true, + "requires": { + "convert-source-map": "1.1.3", + "inline-source-map": "0.6.2", + "lodash.memoize": "3.0.4", + "source-map": "0.5.7" + } + }, + "concat-stream": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.0.6", + "typedarray": "0.0.6" + } + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "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=", + "dev": true + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "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": { + "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.1" + } + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-valid-glob": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", + "integrity": "sha1-1LVcafUYhvm2XHDWwmItN+KfSP4=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", + "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", + "dev": true, + "requires": { + "argparse": "1.0.10", + "esprima": "4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "jsdom": { + "version": "11.6.2", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.6.2.tgz", + "integrity": "sha512-pAeZhpbSlUp5yQcS6cBQJwkbzmv4tWFaYxHbFVSxzXefqjvtRA851Z5N2P+TguVG9YeUDcgb8pdeVQRJh0XR3Q==", + "dev": true, + "requires": { + "abab": "1.0.4", + "acorn": "5.5.3", + "acorn-globals": "4.1.0", + "array-equal": "1.0.0", + "browser-process-hrtime": "0.1.2", + "content-type-parser": "1.0.2", + "cssom": "0.3.2", + "cssstyle": "0.2.37", + "domexception": "1.0.1", + "escodegen": "1.9.1", + "html-encoding-sniffer": "1.0.2", + "left-pad": "1.2.0", + "nwmatcher": "1.4.3", + "parse5": "4.0.0", + "pn": "1.1.0", + "request": "2.83.0", + "request-promise-native": "1.0.5", + "sax": "1.2.4", + "symbol-tree": "3.2.2", + "tough-cookie": "2.3.4", + "w3c-hr-time": "1.0.1", + "webidl-conversions": "4.0.2", + "whatwg-encoding": "1.0.3", + "whatwg-url": "6.4.0", + "ws": "4.1.0", + "xml-name-validator": "3.0.0" + }, + "dependencies": { + "acorn": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", + "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", + "dev": true + } + } + }, + "jsdom-global": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsdom-global/-/jsdom-global-3.0.2.tgz", + "integrity": "sha1-a9KZwTsMRiay2iwDk81DhdYGrLk=", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stable-stringify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", + "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=", + "dev": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + }, + "labeled-stream-splicer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz", + "integrity": "sha1-pS4dE4AkwAuGscDJH2d5GLiuClk=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "isarray": "0.0.1", + "stream-splicer": "2.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "dev": true, + "requires": { + "readable-stream": "2.3.5" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "1.0.0" + } + }, + "lcov-result-merger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcov-result-merger/-/lcov-result-merger-2.0.0.tgz", + "integrity": "sha512-CMjYOcPtl0G3SLrPWYDtZ4zyhsy/2heUf6RKwvyDcJRfyUBQbqLhAlS2PBhTKVCxGp7Ri8AYJ5SGNVEtbEo0fA==", + "dev": true, + "requires": { + "through2": "2.0.3", + "vinyl": "2.1.0", + "vinyl-fs": "2.4.4" + } + }, + "left-pad": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.2.0.tgz", + "integrity": "sha1-0wpzxrggHY99jnlWupYWCHpo4O4=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2", + "type-check": "0.3.2" + } + }, + "lexical-scope": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.2.0.tgz", + "integrity": "sha1-/Ope3HBKSzqHls3KQZw6CvryLfQ=", + "dev": true, + "requires": { + "astw": "2.2.0" + } + }, + "lodash": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", + "dev": true + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", + "dev": true + }, + "lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", + "dev": true + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, + "long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", + "dev": true + }, + "make-error": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.4.tgz", + "integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==", + "dev": true + }, + "md5.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "dev": true, + "requires": { + "hash-base": "3.0.4", + "inherits": "2.0.3" + }, + "dependencies": { + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + } + } + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "dev": true, + "requires": { + "readable-stream": "2.3.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + } + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "brorand": "1.1.0" + } + }, + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "dev": true + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dev": true, + "requires": { + "mime-db": "1.33.0" + } + }, + "minimalistic-assert": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "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" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "mocha": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.4.tgz", + "integrity": "sha512-nMOpAPFosU1B4Ix1jdhx5e3q7XO55ic5a8cgYvW27CequcEY+BabS0kUVL1Cw1V5PuVHZWeNRWFLmEPexo79VA==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + } + }, + "module-deps": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.0.0.tgz", + "integrity": "sha512-BKsMhJJENEM4dTgqq2MDTTHXRHcNUFegoAwlG4HO4VMdUyMcJDKgfgI+MOv6tR5Iv8G3MKZFgsSiyP3ZoosRMw==", + "dev": true, + "requires": { + "JSONStream": "1.3.2", + "browser-resolve": "1.11.2", + "cached-path-relative": "1.0.1", + "concat-stream": "1.6.1", + "defined": "1.0.0", + "detective": "5.1.0", + "duplexer2": "0.1.4", + "inherits": "2.0.3", + "parents": "1.0.1", + "readable-stream": "2.3.5", + "resolve": "1.5.0", + "stream-combiner2": "1.1.1", + "subarg": "1.0.0", + "through2": "2.0.3", + "xtend": "4.0.1" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nan": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.9.2.tgz", + "integrity": "sha512-ltW65co7f3PQWBDbqVvaU1WtFJUsNW7sWWm4HINhbMQIyVyzIeyZ8toX5TC5eeooE6piZoaEh4cZkueSKG3KYw==", + "dev": true + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "1.1.0" + } + }, + "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 + }, + "nwmatcher": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.3.tgz", + "integrity": "sha512-IKdSTiDWCarf2JTS5e9e2+5tPZGdkRJ79XjYV0pzK8Q9BpsFyBq1RGKxzs7Q8UBushGw7m6TzVKz6fcY99iSWw==", + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "0.1.5", + "is-extendable": "0.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" + } + }, + "optjs": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz", + "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4=", + "dev": true + }, + "ordered-read-streams": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", + "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", + "dev": true, + "requires": { + "is-stream": "1.1.0", + "readable-stream": "2.3.5" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "1.0.0" + } + }, + "pako": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", + "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", + "dev": true + }, + "parents": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", + "dev": true, + "requires": { + "path-platform": "0.11.15" + } + }, + "parse-asn1": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", + "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", + "dev": true, + "requires": { + "asn1.js": "4.10.1", + "browserify-aes": "1.1.1", + "create-hash": "1.1.3", + "evp_bytestokey": "1.0.3", + "pbkdf2": "3.0.14" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + } + } + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, + "path-platform": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", + "dev": true + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, + "pbkdf2": { + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", + "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==", + "dev": true, + "requires": { + "create-hash": "1.1.3", + "create-hmac": "1.1.6", + "ripemd160": "2.0.1", + "safe-buffer": "5.1.1", + "sha.js": "2.4.10" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "promise-polyfill": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-7.1.0.tgz", + "integrity": "sha512-P6NJ2wU/8fac44ENORsuqT8TiolKGB2u0fEClPtXezn7w5cmLIjM/7mhPlTebke2EPr6tmqZbXvnX0TxwykGrg==", + "dev": true + }, + "protobufjs": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.2.tgz", + "integrity": "sha1-WXSNfc8D0tsiwT2p/rAk4Wq4DJE=", + "dev": true, + "requires": { + "ascli": "1.0.1", + "bytebuffer": "5.0.1", + "glob": "7.1.2", + "yargs": "3.32.0" + } + }, + "public-encrypt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", + "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.1.3", + "parse-asn1": "5.1.0", + "randombytes": "2.0.6" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "randomatic": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "dev": true, + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "2.0.6", + "safe-buffer": "5.1.1" + } + }, + "read-only-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", + "dev": true, + "requires": { + "readable-stream": "2.3.5" + } + }, + "readable-stream": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz", + "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "0.1.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "request": { + "version": "2.83.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", + "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", + "dev": true, + "requires": { + "aws-sign2": "0.7.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.2", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.18", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.1", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.4", + "tunnel-agent": "0.6.0", + "uuid": "3.2.1" + } + }, + "request-promise-core": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", + "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", + "dev": true, + "requires": { + "lodash": "4.17.5" + } + }, + "request-promise-native": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", + "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", + "dev": true, + "requires": { + "request-promise-core": "1.1.1", + "stealthy-require": "1.1.1", + "tough-cookie": "2.3.4" + } + }, + "resolve": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", + "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", + "dev": true, + "requires": { + "path-parse": "1.0.5" + } + }, + "ripemd160": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", + "dev": true, + "requires": { + "hash-base": "2.0.2", + "inherits": "2.0.3" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "sha.js": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.10.tgz", + "integrity": "sha512-vnwmrFDlOExK4Nm16J2KMWHLrp14lBrjxMxBJpu++EnsuBmpiYaM/MEs46Vxxm/4FvdP5yTwuCTO9it5FSjrqA==", + "dev": true, + "requires": { + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + }, + "shasum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz", + "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=", + "dev": true, + "requires": { + "json-stable-stringify": "0.0.1", + "sha.js": "2.4.10" + } + }, + "shell-quote": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", + "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", + "dev": true, + "requires": { + "array-filter": "0.0.1", + "array-map": "0.0.0", + "array-reduce": "0.0.0", + "jsonify": "0.0.0" + } + }, + "sntp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", + "dev": true, + "requires": { + "hoek": "4.2.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-support": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.3.tgz", + "integrity": "sha512-eKkTgWYeBOQqFGXRfKabMFdnWepo51vWqEdoeikaEPFiJC7MCU5j2h4+6Q8npkZTeLGbSyecZvRxiSoWl3rh+w==", + "dev": true, + "requires": { + "source-map": "0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "dev": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + } + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5" + } + }, + "stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "dev": true, + "requires": { + "duplexer2": "0.1.4", + "readable-stream": "2.3.5" + } + }, + "stream-http": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.0.tgz", + "integrity": "sha512-sZOFxI/5xw058XIRHl4dU3dZ+TTOIGJR78Dvo0oEAejIt4ou27k+3ne1zYmCV+v7UucbxIFQuOgnkTVHh8YPnw==", + "dev": true, + "requires": { + "builtin-status-codes": "3.0.0", + "inherits": "2.0.3", + "readable-stream": "2.3.5", + "to-arraybuffer": "1.0.1", + "xtend": "4.0.1" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "stream-splicer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz", + "integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "strip-bom-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", + "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", + "dev": true, + "requires": { + "first-chunk-stream": "1.0.0", + "strip-bom": "2.0.0" + } + }, + "subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", + "dev": true, + "requires": { + "minimist": "1.2.0" + } + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + }, + "symbol-tree": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", + "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", + "dev": true + }, + "syntax-error": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", + "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", + "dev": true, + "requires": { + "acorn-node": "1.3.0" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "2.3.5", + "xtend": "4.0.1" + } + }, + "through2-filter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", + "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", + "dev": true, + "requires": { + "through2": "2.0.3", + "xtend": "4.0.1" + } + }, + "timers-browserify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", + "dev": true, + "requires": { + "process": "0.11.10" + } + }, + "to-absolute-glob": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz", + "integrity": "sha1-HN+kcqnvUMI57maZm2YsoOs5k38=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "dev": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "dev": true, + "requires": { + "punycode": "2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", + "dev": true + } + } + }, + "ts-node": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-5.0.1.tgz", + "integrity": "sha512-XK7QmDcNHVmZkVtkiwNDWiERRHPyU8nBqZB1+iv2UhOG0q3RQ9HsZ2CMqISlFbxjrYFGfG2mX7bW4dAyxBVzUw==", + "dev": true, + "requires": { + "arrify": "1.0.1", + "chalk": "2.3.2", + "diff": "3.5.0", + "make-error": "1.3.4", + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "source-map-support": "0.5.3", + "yn": "2.0.0" + } + }, + "tslib": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", + "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==", + "dev": true + }, + "tslint": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.9.1.tgz", + "integrity": "sha1-ElX4ej/1frCw4fDmEKi0dIBGya4=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "builtin-modules": "1.1.1", + "chalk": "2.3.2", + "commander": "2.15.0", + "diff": "3.5.0", + "glob": "7.1.2", + "js-yaml": "3.11.0", + "minimatch": "3.0.4", + "resolve": "1.5.0", + "semver": "5.5.0", + "tslib": "1.9.0", + "tsutils": "2.22.2" + }, + "dependencies": { + "commander": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.0.tgz", + "integrity": "sha512-7B1ilBwtYSbetCgTY1NJFg+gVpestg0fdA1MhC1Vs4ssyfSXnCAjFr+QcQM9/RedXC0EaUx1sG8Smgw2VfgKEg==", + "dev": true + } + } + }, + "tsutils": { + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.22.2.tgz", + "integrity": "sha512-u06FUSulCJ+Y8a2ftuqZN6kIGqdP2yJjUPEngXqmdPND4UQfb04igcotH+dw+IFr417yP6muCLE8/5/Qlfnx0w==", + "dev": true, + "requires": { + "tslib": "1.9.0" + } + }, + "tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2" + } + }, + "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 + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz", + "integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==", + "dev": true + }, + "uglify-js": { + "version": "3.3.13", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.13.tgz", + "integrity": "sha512-7rdn/bDOG1ElSTPdh7AI5TCjLv63ZD4k8BBadN3ssIkhlaQL2c0yRxmXCyOYhZK0wZTgGgUSnYQ4CGu+Jos5cA==", + "dev": true, + "requires": { + "commander": "2.14.1", + "source-map": "0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz", + "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "umd": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.1.tgz", + "integrity": "sha1-iuVW4RAR9jwllnCKiDclnwGz1g4=", + "dev": true + }, + "unique-stream": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", + "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", + "dev": true, + "requires": { + "json-stable-stringify": "1.0.1", + "through2-filter": "2.0.0" + }, + "dependencies": { + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "0.0.0" + } + } + } + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", + "dev": true + }, + "vali-date": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", + "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + } + }, + "vinyl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz", + "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", + "dev": true, + "requires": { + "clone": "2.1.1", + "clone-buffer": "1.0.0", + "clone-stats": "1.0.0", + "cloneable-readable": "1.1.1", + "remove-trailing-separator": "1.1.0", + "replace-ext": "1.0.0" + } + }, + "vinyl-fs": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz", + "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", + "dev": true, + "requires": { + "duplexify": "3.5.4", + "glob-stream": "5.3.5", + "graceful-fs": "4.1.11", + "gulp-sourcemaps": "1.6.0", + "is-valid-glob": "0.3.0", + "lazystream": "1.0.0", + "lodash.isequal": "4.5.0", + "merge-stream": "1.0.1", + "mkdirp": "0.5.1", + "object-assign": "4.1.1", + "readable-stream": "2.3.5", + "strip-bom": "2.0.0", + "strip-bom-stream": "1.0.0", + "through2": "2.0.3", + "through2-filter": "2.0.0", + "vali-date": "1.0.0", + "vinyl": "1.2.0" + }, + "dependencies": { + "clone": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", + "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=", + "dev": true + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "dev": true, + "requires": { + "clone": "1.0.3", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "0.0.1" + } + }, + "w3c-hr-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", + "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "dev": true, + "requires": { + "browser-process-hrtime": "0.1.2" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true + }, + "websocket-driver": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "dev": true, + "requires": { + "http-parser-js": "0.4.11", + "websocket-extensions": "0.1.3" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz", + "integrity": "sha512-jLBwwKUhi8WtBfsMQlL4bUUcT8sMkAtQinscJAe/M4KHCkHuUJAF6vuB0tueNIw4c8ziO6AkRmgY+jL3a0iiPw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.19" + } + }, + "whatwg-url": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.4.0.tgz", + "integrity": "sha512-Z0CVh/YE217Foyb488eo+iBv+r7eAQ0wSTyApi9n06jhcA3z6Nidg/EGvl0UFkg7kMdKxfBzzr+o9JF+cevgMg==", + "dev": true, + "requires": { + "lodash.sortby": "4.7.0", + "tr46": "1.0.1", + "webidl-conversions": "4.0.2" + } + }, + "window-size": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", + "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "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=", + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz", + "integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==", + "dev": true, + "requires": { + "async-limiter": "1.0.0", + "safe-buffer": "5.1.1" + } + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", + "dev": true, + "requires": { + "camelcase": "2.1.1", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "os-locale": "1.4.0", + "string-width": "1.0.2", + "window-size": "0.1.4", + "y18n": "3.2.1" + } + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + } + } +} diff --git a/package.json b/package.json index e6e2433f..3f8c5e3e 100644 --- a/package.json +++ b/package.json @@ -19,40 +19,36 @@ "realtime", "geolocation" ], - "main": "dist/geofire.js", + "main": "dist/index.js", "files": [ "dist/**", "LICENSE", "README.md", "package.json" ], - "dependencies": {}, "peerDependencies": { - "firebase": "^2.4.0 || 3.x.x" + "firebase": "^4.x.x" }, "devDependencies": { - "coveralls": "2.11.4", - "firebase": "3.x.x", - "gulp": "3.9.0", - "gulp-concat": "2.6.0", - "gulp-ext-replace": "0.2.0", - "gulp-jshint": "1.11.2", - "gulp-karma": "0.0.5", - "gulp-uglify": "1.4.1", - "jasmine-core": "^2.5.2", - "jshint-stylish": "2.0.1", - "karma": "^0.13.22", - "karma-chrome-launcher": "0.2.0", - "karma-coverage": "0.5.2", - "karma-failed-reporter": "0.0.3", - "karma-firefox-launcher": "0.1.6", - "karma-jasmine": "0.3.6", - "karma-spec-reporter": "0.0.20", - "run-sequence": "^1.1.4", - "streamqueue": "0.1.1" + "@types/chai": "^4.1.2", + "@types/mocha": "^2.2.48", + "@types/node": "^9.4.6", + "browserify": "^16.1.1", + "chai": "^4.1.2", + "firebase": "4.x.x", + "jsdom": "^11.6.2", + "jsdom-global": "^3.0.2", + "mocha": "^5.0.4", + "ts-node": "^5.0.1", + "tslint": "^5.9.1", + "typescript": "^2.7.2", + "uglify-js": "^3.3.13" }, "scripts": { - "test": "gulp test", - "travis": "gulp" + "browserify": "browserify dist/index.js -o dist/geofire.js", + "build": "tsc && npm run browserify && npm run uglify", + "test": "mocha --reporter spec -r ts-node/register -r jsdom-global/register --timeout 5000 'tests/**/*.test.ts'", + "travis": "npm run test && npm run build", + "uglify": "uglifyjs dist/geofire.js -c -m -o dist/geofire.min.js" } } diff --git a/src/firebase/geoCallbackRegistration.ts b/src/firebase/geoCallbackRegistration.ts new file mode 100644 index 00000000..9e25697c --- /dev/null +++ b/src/firebase/geoCallbackRegistration.ts @@ -0,0 +1,27 @@ +/** + * Creates a GeoCallbackRegistration instance. + */ +export class GeoCallbackRegistration { + /** + * @param _cancelCallback Callback to run when this callback registration is cancelled. + */ + constructor(private _cancelCallback: Function) { + if (typeof this._cancelCallback !== 'function') { + throw new Error('callback must be a function'); + } + } + + /********************/ + /* PUBLIC METHODS */ + /********************/ + /** + * Cancels this callback registration so that it no longer fires its callback. This + * has no effect on any other callback registrations you may have created. + */ + public cancel(): void { + if (typeof this._cancelCallback !== 'undefined') { + this._cancelCallback(); + this._cancelCallback = undefined; + } + } +} \ No newline at end of file diff --git a/src/firebase/geoFire.ts b/src/firebase/geoFire.ts new file mode 100644 index 00000000..4abb27bf --- /dev/null +++ b/src/firebase/geoFire.ts @@ -0,0 +1,148 @@ +import * as firebase from 'firebase'; + +import { GeoQuery } from './geoQuery'; +import { decodeGeoFireObject, degreesToRadians, encodeGeoFireObject, encodeGeohash, validateLocation, validateKey } from './geoFireUtils'; + +import { QueryCriteria } from '../interfaces'; + +/** + * Creates a GeoFire instance. + */ +export class GeoFire { + /** + * @param _firebaseRef A Firebase reference where the GeoFire data will be stored. + */ + constructor(private _firebaseRef: firebase.database.Reference) { + if (Object.prototype.toString.call(this._firebaseRef) !== '[object Object]') { + throw new Error('firebaseRef must be an instance of Firebase'); + } + } + + /********************/ + /* PUBLIC METHODS */ + /********************/ + /** + * Returns a promise fulfilled with the location corresponding to the provided key. + * + * If the provided key does not exist, the returned promise is fulfilled with null. + * + * @param key The key of the location to retrieve. + * @returns A promise that is fulfilled with the location of the given key. + */ + public get(key: string): Promise { + validateKey(key); + return this._firebaseRef.child(key).once('value').then((dataSnapshot: firebase.database.DataSnapshot) => { + const snapshotVal = dataSnapshot.val(); + if (snapshotVal === null) { + return null; + } else { + return decodeGeoFireObject(snapshotVal); + } + }); + }; + + /** + * Returns the Firebase instance used to create this GeoFire instance. + * + * @returns The Firebase instance used to create this GeoFire instance. + */ + public ref(): firebase.database.Reference { + return this._firebaseRef; + }; + + /** + * Removes the provided key from this GeoFire. Returns an empty promise fulfilled when the key has been removed. + * + * If the provided key is not in this GeoFire, the promise will still successfully resolve. + * + * @param key The key of the location to remove. + * @returns A promise that is fulfilled after the inputted key is removed. + */ + public remove(key: string): Promise { + return this.set(key, null); + }; + + /** + * Adds the provided key - location pair(s) to Firebase. Returns an empty promise which is fulfilled when the write is complete. + * + * If any provided key already exists in this GeoFire, it will be overwritten with the new location value. + * + * @param keyOrLocations The key representing the location to add or a mapping of key - location pairs which + * represent the locations to add. + * @param location The [latitude, longitude] pair to add. + * @returns A promise that is fulfilled when the write is complete. + */ + public set(keyOrLocations: string | any, location?: number[]): Promise { + let locations; + if (typeof keyOrLocations === 'string' && keyOrLocations.length !== 0) { + // If this is a set for a single location, convert it into a object + locations = {}; + locations[keyOrLocations] = location; + } else if (typeof keyOrLocations === 'object') { + if (typeof location !== 'undefined') { + throw new Error('The location argument should not be used if you pass an object to set().'); + } + locations = keyOrLocations; + } else { + throw new Error('keyOrLocations must be a string or a mapping of key - location pairs.'); + } + + const newData = {}; + + Object.keys(locations).forEach(function (key) { + validateKey(key); + + const location: number[] = locations[key]; + if (location === null) { + // Setting location to null is valid since it will remove the key + newData[key] = null; + } else { + validateLocation(location); + + const geohash: string = encodeGeohash(location); + newData[key] = encodeGeoFireObject(location, geohash); + } + }); + + return this._firebaseRef.update(newData); + }; + + /** + * Returns a new GeoQuery instance with the provided queryCriteria. + * + * @param queryCriteria The criteria which specifies the GeoQuery's center and radius. + * @return A new GeoQuery object. + */ + public query(queryCriteria: QueryCriteria): GeoQuery { + return new GeoQuery(this._firebaseRef, queryCriteria); + }; + + /********************/ + /* STATIC METHODS */ + /********************/ + /** + * Static method which calculates the distance, in kilometers, between two locations, + * via the Haversine formula. Note that this is approximate due to the fact that the + * Earth's radius varies between 6356.752 km and 6378.137 km. + * + * @param location1 The [latitude, longitude] pair of the first location. + * @param location2 The [latitude, longitude] pair of the second location. + * @returns The distance, in kilometers, between the inputted locations. + */ + static distance(location1: number[], location2: number[]) { + validateLocation(location1); + validateLocation(location2); + + var radius = 6371; // Earth's radius in kilometers + var latDelta = degreesToRadians(location2[0] - location1[0]); + var lonDelta = degreesToRadians(location2[1] - location1[1]); + + var a = (Math.sin(latDelta / 2) * Math.sin(latDelta / 2)) + + (Math.cos(degreesToRadians(location1[0])) * Math.cos(degreesToRadians(location2[0])) * + Math.sin(lonDelta / 2) * Math.sin(lonDelta / 2)); + + var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + + return radius * c; + }; +} diff --git a/src/firebase/geoFireUtils.ts b/src/firebase/geoFireUtils.ts new file mode 100644 index 00000000..849a6abd --- /dev/null +++ b/src/firebase/geoFireUtils.ts @@ -0,0 +1,437 @@ +import * as firebase from 'firebase'; + +import { GeoFireObj } from '../interfaces'; + +// Default geohash length +export const g_GEOHASH_PRECISION: number = 10; + +// Characters used in location geohashes +export const g_BASE32: string = '0123456789bcdefghjkmnpqrstuvwxyz'; + +// The meridional circumference of the earth in meters +export const g_EARTH_MERI_CIRCUMFERENCE: number = 40007860; + +// Length of a degree latitude at the equator +export const g_METERS_PER_DEGREE_LATITUDE: number = 110574; + +// Number of bits per geohash character +export const g_BITS_PER_CHAR: number = 5; + +// Maximum length of a geohash in bits +export const g_MAXIMUM_BITS_PRECISION: number = 22 * g_BITS_PER_CHAR; + +// Equatorial radius of the earth in meters +export const g_EARTH_EQ_RADIUS: number = 6378137.0; + +// The following value assumes a polar radius of +// const g_EARTH_POL_RADIUS = 6356752.3; +// The formulate to calculate g_E2 is +// g_E2 == (g_EARTH_EQ_RADIUS^2-g_EARTH_POL_RADIUS^2)/(g_EARTH_EQ_RADIUS^2) +// The exact value is used here to avoid rounding errors +export const g_E2: number = 0.00669447819799; + +// Cutoff for rounding errors on double calculations +export const g_EPSILON: number = 1e-12; + +Math.log2 = Math.log2 || function (x) { + return Math.log(x) / Math.log(2); +}; + +/** + * Validates the inputted key and throws an error if it is invalid. + * + * @param key The key to be verified. + */ +export function validateKey(key: string): void { + let error: string; + + if (typeof key !== 'string') { + error = 'key must be a string'; + } else if (key.length === 0) { + error = 'key cannot be the empty string'; + } else if (1 + g_GEOHASH_PRECISION + key.length > 755) { + // Firebase can only stored child paths up to 768 characters + // The child path for this key is at the least: 'i/key' + error = 'key is too long to be stored in Firebase'; + } else if (/[\[\].#$\/\u0000-\u001F\u007F]/.test(key)) { + // Firebase does not allow node keys to contain the following characters + error = 'key cannot contain any of the following characters: . # $ ] [ /'; + } + + if (typeof error !== 'undefined') { + throw new Error('Invalid GeoFire key \'' + key + '\': ' + error); + } +}; + +/** + * Validates the inputted location and throws an error if it is invalid. + * + * @param location The [latitude, longitude] pair to be verified. + */ +export function validateLocation(location: number[]): void { + let error: string; + + if (!Array.isArray(location)) { + error = 'location must be an array'; + } else if (location.length !== 2) { + error = 'expected array of length 2, got length ' + location.length; + } else { + const latitude = location[0]; + const longitude = location[1]; + + if (typeof latitude !== 'number' || isNaN(latitude)) { + error = 'latitude must be a number'; + } else if (latitude < -90 || latitude > 90) { + error = 'latitude must be within the range [-90, 90]'; + } else if (typeof longitude !== 'number' || isNaN(longitude)) { + error = 'longitude must be a number'; + } else if (longitude < -180 || longitude > 180) { + error = 'longitude must be within the range [-180, 180]'; + } + } + + if (typeof error !== 'undefined') { + throw new Error('Invalid GeoFire location \'' + location + '\': ' + error); + } +}; + +/** + * Validates the inputted geohash and throws an error if it is invalid. + * + * @param geohash The geohash to be validated. + */ +export function validateGeohash(geohash: string): void { + let error; + + if (typeof geohash !== 'string') { + error = 'geohash must be a string'; + } else if (geohash.length === 0) { + error = 'geohash cannot be the empty string'; + } else { + for (const letter of geohash) { + if (g_BASE32.indexOf(letter) === -1) { + error = 'geohash cannot contain \'' + letter + '\''; + } + } + } + + if (typeof error !== 'undefined') { + throw new Error('Invalid GeoFire geohash \'' + geohash + '\': ' + error); + } +}; + +/** + * Validates the inputted query criteria and throws an error if it is invalid. + * + * @param newQueryCriteria The criteria which specifies the query's center and/or radius. + * @param requireCenterAndRadius The criteria which center and radius required. + */ +export function validateCriteria(newQueryCriteria: any, requireCenterAndRadius: boolean = false): void { + if (typeof newQueryCriteria !== 'object') { + throw new Error('query criteria must be an object'); + } else if (typeof newQueryCriteria.center === 'undefined' && typeof newQueryCriteria.radius === 'undefined') { + throw new Error('radius and/or center must be specified'); + } else if (requireCenterAndRadius && (typeof newQueryCriteria.center === 'undefined' || typeof newQueryCriteria.radius === 'undefined')) { + throw new Error('query criteria for a new query must contain both a center and a radius'); + } + + // Throw an error if there are any extraneous attributes + const keys: string[] = Object.keys(newQueryCriteria); + for (const key of keys) { + if (key !== 'center' && key !== 'radius') { + throw new Error('Unexpected attribute \'' + key + '\' found in query criteria'); + } + } + + // Validate the 'center' attribute + if (typeof newQueryCriteria.center !== 'undefined') { + validateLocation(newQueryCriteria.center); + } + + // Validate the 'radius' attribute + if (typeof newQueryCriteria.radius !== 'undefined') { + if (typeof newQueryCriteria.radius !== 'number' || isNaN(newQueryCriteria.radius)) { + throw new Error('radius must be a number'); + } else if (newQueryCriteria.radius < 0) { + throw new Error('radius must be greater than or equal to 0'); + } + } +}; + +/** + * Converts degrees to radians. + * + * @param degrees The number of degrees to be converted to radians. + * @returns The number of radians equal to the inputted number of degrees. + */ +export function degreesToRadians(degrees: number): number { + if (typeof degrees !== 'number' || isNaN(degrees)) { + throw new Error('Error: degrees must be a number'); + } + + return (degrees * Math.PI / 180); +}; + +/** + * Generates a geohash of the specified precision/string length from the [latitude, longitude] + * pair, specified as an array. + * + * @param location The [latitude, longitude] pair to encode into a geohash. + * @param precision The length of the geohash to create. If no precision is specified, the + * global default is used. + * @returns The geohash of the inputted location. + */ +export function encodeGeohash(location: number[], precision: number = g_GEOHASH_PRECISION): string { + validateLocation(location); + if (typeof precision !== 'undefined') { + if (typeof precision !== 'number' || isNaN(precision)) { + throw new Error('precision must be a number'); + } else if (precision <= 0) { + throw new Error('precision must be greater than 0'); + } else if (precision > 22) { + throw new Error('precision cannot be greater than 22'); + } else if (Math.round(precision) !== precision) { + throw new Error('precision must be an integer'); + } + } + + const latitudeRange = { + min: -90, + max: 90 + }; + const longitudeRange = { + min: -180, + max: 180 + }; + let hash: string = ''; + let hashVal = 0; + let bits: number = 0; + let even: number | boolean = 1; + + while (hash.length < precision) { + const val = even ? location[1] : location[0]; + const range = even ? longitudeRange : latitudeRange; + const mid = (range.min + range.max) / 2; + + if (val > mid) { + hashVal = (hashVal << 1) + 1; + range.min = mid; + } else { + hashVal = (hashVal << 1) + 0; + range.max = mid; + } + + even = !even; + if (bits < 4) { + bits++; + } else { + bits = 0; + hash += g_BASE32[hashVal]; + hashVal = 0; + } + } + + return hash; +}; + +/** + * Calculates the number of degrees a given distance is at a given latitude. + * + * @param distance The distance to convert. + * @param latitude The latitude at which to calculate. + * @returns The number of degrees the distance corresponds to. + */ +export function metersToLongitudeDegrees(distance: number, latitude: number): number { + const radians = degreesToRadians(latitude); + const num = Math.cos(radians) * g_EARTH_EQ_RADIUS * Math.PI / 180; + const denom = 1 / Math.sqrt(1 - g_E2 * Math.sin(radians) * Math.sin(radians)); + const deltaDeg = num * denom; + if (deltaDeg < g_EPSILON) { + return distance > 0 ? 360 : 0; + } + else { + return Math.min(360, distance / deltaDeg); + } +}; + +/** + * Calculates the bits necessary to reach a given resolution, in meters, for the longitude at a + * given latitude. + * + * @param resolution The desired resolution. + * @param latitude The latitude used in the conversion. + * @return The bits necessary to reach a given resolution, in meters. + */ +export function longitudeBitsForResolution(resolution: number, latitude: number): number { + const degs = metersToLongitudeDegrees(resolution, latitude); + return (Math.abs(degs) > 0.000001) ? Math.max(1, Math.log2(360 / degs)) : 1; +}; + +/** + * Calculates the bits necessary to reach a given resolution, in meters, for the latitude. + * + * @param resolution The bits necessary to reach a given resolution, in meters. + * @returns Bits necessary to reach a given resolution, in meters, for the latitude. + */ +export function latitudeBitsForResolution(resolution: number): number { + return Math.min(Math.log2(g_EARTH_MERI_CIRCUMFERENCE / 2 / resolution), g_MAXIMUM_BITS_PRECISION); +}; + +/** + * Wraps the longitude to [-180,180]. + * + * @param longitude The longitude to wrap. + * @returns longitude The resulting longitude. + */ +export function wrapLongitude(longitude: number): number { + if (longitude <= 180 && longitude >= -180) { + return longitude; + } + const adjusted = longitude + 180; + if (adjusted > 0) { + return (adjusted % 360) - 180; + } + else { + return 180 - (-adjusted % 360); + } +}; + +/** + * Calculates the maximum number of bits of a geohash to get a bounding box that is larger than a + * given size at the given coordinate. + * + * @param coordinate The coordinate as a [latitude, longitude] pair. + * @param size The size of the bounding box. + * @returns The number of bits necessary for the geohash. + */ +export function boundingBoxBits(coordinate: number[], size: number): number { + const latDeltaDegrees = size / g_METERS_PER_DEGREE_LATITUDE; + const latitudeNorth = Math.min(90, coordinate[0] + latDeltaDegrees); + const latitudeSouth = Math.max(-90, coordinate[0] - latDeltaDegrees); + const bitsLat = Math.floor(latitudeBitsForResolution(size)) * 2; + const bitsLongNorth = Math.floor(longitudeBitsForResolution(size, latitudeNorth)) * 2 - 1; + const bitsLongSouth = Math.floor(longitudeBitsForResolution(size, latitudeSouth)) * 2 - 1; + return Math.min(bitsLat, bitsLongNorth, bitsLongSouth, g_MAXIMUM_BITS_PRECISION); +}; + +/** + * Calculates eight points on the bounding box and the center of a given circle. At least one + * geohash of these nine coordinates, truncated to a precision of at most radius, are guaranteed + * to be prefixes of any geohash that lies within the circle. + * + * @param center The center given as [latitude, longitude]. + * @param radius The radius of the circle. + * @returns The eight bounding box points. + */ +export function boundingBoxCoordinates(center: number[], radius: number): number[][] { + const latDegrees = radius / g_METERS_PER_DEGREE_LATITUDE; + const latitudeNorth = Math.min(90, center[0] + latDegrees); + const latitudeSouth = Math.max(-90, center[0] - latDegrees); + const longDegsNorth = metersToLongitudeDegrees(radius, latitudeNorth); + const longDegsSouth = metersToLongitudeDegrees(radius, latitudeSouth); + const longDegs = Math.max(longDegsNorth, longDegsSouth); + return [ + [center[0], center[1]], + [center[0], wrapLongitude(center[1] - longDegs)], + [center[0], wrapLongitude(center[1] + longDegs)], + [latitudeNorth, center[1]], + [latitudeNorth, wrapLongitude(center[1] - longDegs)], + [latitudeNorth, wrapLongitude(center[1] + longDegs)], + [latitudeSouth, center[1]], + [latitudeSouth, wrapLongitude(center[1] - longDegs)], + [latitudeSouth, wrapLongitude(center[1] + longDegs)] + ]; +}; + +/** + * Calculates the bounding box query for a geohash with x bits precision. + * + * @param geohash The geohash whose bounding box query to generate. + * @param bits The number of bits of precision. + * @returns A [start, end] pair of geohashes. + */ +export function geohashQuery(geohash: string, bits: number): string[] { + validateGeohash(geohash); + const precision = Math.ceil(bits / g_BITS_PER_CHAR); + if (geohash.length < precision) { + return [geohash, geohash + '~']; + } + geohash = geohash.substring(0, precision); + const base = geohash.substring(0, geohash.length - 1); + const lastValue = g_BASE32.indexOf(geohash.charAt(geohash.length - 1)); + const significantBits = bits - (base.length * g_BITS_PER_CHAR); + const unusedBits = (g_BITS_PER_CHAR - significantBits); + // delete unused bits + const startValue = (lastValue >> unusedBits) << unusedBits; + const endValue = startValue + (1 << unusedBits); + if (endValue > 31) { + return [base + g_BASE32[startValue], base + '~']; + } else { + return [base + g_BASE32[startValue], base + g_BASE32[endValue]]; + } +}; + +/** + * Calculates a set of queries to fully contain a given circle. A query is a [start, end] pair + * where any geohash is guaranteed to be lexiographically larger then start and smaller than end. + * + * @param center The center given as [latitude, longitude] pair. + * @param radius The radius of the circle. + * @return An array of geohashes containing a [start, end] pair. + */ +export function geohashQueries(center: number[], radius: number): string[][] { + validateLocation(center); + const queryBits = Math.max(1, boundingBoxBits(center, radius)); + const geohashPrecision = Math.ceil(queryBits / g_BITS_PER_CHAR); + const coordinates = boundingBoxCoordinates(center, radius); + const queries = coordinates.map(function (coordinate) { + return geohashQuery(encodeGeohash(coordinate, geohashPrecision), queryBits); + }); + // remove duplicates + return queries.filter(function (query, index) { + return !queries.some(function (other, otherIndex) { + return index > otherIndex && query[0] === other[0] && query[1] === other[1]; + }); + }); +}; + +/** + * Encodes a location and geohash as a GeoFire object. + * + * @param location The location as [latitude, longitude] pair. + * @param geohash The geohash of the location. + * @returns The location encoded as GeoFire object. + */ +export function encodeGeoFireObject(location: number[], geohash: string): GeoFireObj { + validateLocation(location); + validateGeohash(geohash); + return { '.priority': geohash, 'g': geohash, 'l': location }; +} + +/** + * Decodes the location given as GeoFire object. Returns null if decoding fails. + * + * @param geoFireObj The location encoded as GeoFire object. + * @returns The location as [latitude, longitude] pair or null if decoding fails. + */ +export function decodeGeoFireObject(geoFireObj: GeoFireObj): number[] { + if (geoFireObj !== null && geoFireObj.hasOwnProperty('l') && Array.isArray(geoFireObj.l) && geoFireObj.l.length === 2) { + return geoFireObj.l; + } else { + throw new Error('Unexpected GeoFire location object encountered: ' + JSON.stringify(geoFireObj)); + } +} + +/** + * Returns the key of a Firebase snapshot across SDK versions. + * + * @param A Firebase snapshot. + * @returns key The Firebase snapshot's key. + */ +export function getKey(snapshot: firebase.database.DataSnapshot): string { + let key: string; + if (typeof snapshot.key === 'string' || snapshot.key === null) { + key = snapshot.key; + } + return key; +} \ No newline at end of file diff --git a/src/firebase/geoQuery.ts b/src/firebase/geoQuery.ts new file mode 100644 index 00000000..70b7f56e --- /dev/null +++ b/src/firebase/geoQuery.ts @@ -0,0 +1,527 @@ +import * as firebase from 'firebase'; + +import { GeoFire } from './geoFire'; +import { GeoCallbackRegistration } from './geoCallbackRegistration'; +import { decodeGeoFireObject, encodeGeohash, getKey, geohashQueries, validateCriteria, validateLocation } from './geoFireUtils'; + +import { QueryCriteria } from '../interfaces'; + +/** + * Creates a GeoQuery instance. + */ +export class GeoQuery { + // Event callbacks + private _callbacks: any = { ready: [], key_entered: [], key_exited: [], key_moved: [] }; + // Variable to track when the query is cancelled + private _cancelled: boolean = false; + private _center: number[]; + // A dictionary of geohash queries which currently have an active callbacks + private _currentGeohashesQueried: any = {}; + // A dictionary of locations that a currently active in the queries + // Note that not all of these are currently within this query + private _locationsTracked: any = {}; + private _radius: number; + + // Variables used to keep track of when to fire the 'ready' event + private _valueEventFired: boolean = false; + private _outstandingGeohashReadyEvents: any; + // Every ten seconds, clean up the geohashes we are currently querying for. We keep these around + // for a little while since it's likely that they will need to be re-queried shortly after they + // move outside of the query's bounding box. + private _geohashCleanupScheduled: boolean = false; + private _cleanUpCurrentGeohashesQueriedInterval: NodeJS.Timer; + private _cleanUpCurrentGeohashesQueriedTimeout = null; + + /** + * @param _firebaseRef A Firebase reference where the GeoFire data will be stored. + * @param _queryCriteria The criteria which specifies the query's center and radius. + */ + constructor(private _firebaseRef: firebase.database.Reference, private _queryCriteria: QueryCriteria) { + // Firebase reference of the GeoFire which created this query + if (Object.prototype.toString.call(this._firebaseRef) !== '[object Object]') { + throw new Error('firebaseRef must be an instance of Firebase'); + } + + this._cleanUpCurrentGeohashesQueriedInterval = setInterval(() => { + if (this._geohashCleanupScheduled === false) { + this._cleanUpCurrentGeohashesQueried(); + } + }, 10000); + + // Validate and save the query criteria + validateCriteria(_queryCriteria, true); + this._center = _queryCriteria.center; + this._radius = _queryCriteria.radius; + + // Listen for new geohashes being added around this query and fire the appropriate events + this._listenForNewGeohashes(); + } + + /********************/ + /* PUBLIC METHODS */ + /********************/ + /** + * Terminates this query so that it no longer sends location updates. All callbacks attached to this + * query via on() will be cancelled. This query can no longer be used in the future. + */ + public cancel(): void { + // Mark this query as cancelled + this._cancelled = true; + + // Cancel all callbacks in this query's callback list + this._callbacks = { ready: [], key_entered: [], key_exited: [], key_moved: [] }; + + // Turn off all Firebase listeners for the current geohashes being queried + const keys: string[] = Object.keys(this._currentGeohashesQueried); + keys.forEach((geohashQueryStr: string) => { + const query: string[] = this._stringToQuery(geohashQueryStr); + this._cancelGeohashQuery(query, this._currentGeohashesQueried[geohashQueryStr]); + delete this._currentGeohashesQueried[geohashQueryStr]; + }); + + // Delete any stored locations + this._locationsTracked = {}; + + // Turn off the current geohashes queried clean up interval + clearInterval(this._cleanUpCurrentGeohashesQueriedInterval); + }; + + /** + * Returns the location signifying the center of this query. + * + * @returns The [latitude, longitude] pair signifying the center of this query. + */ + public center(): number[] { + return this._center; + }; + + /** + * Attaches a callback to this query which will be run when the provided eventType fires. Valid eventType + * values are 'ready', 'key_entered', 'key_exited', and 'key_moved'. The ready event callback is passed no + * parameters. All other callbacks will be passed three parameters: (1) the location's key, (2) the location's + * [latitude, longitude] pair, and (3) the distance, in kilometers, from the location to this query's center + * + * 'ready' is used to signify that this query has loaded its initial state and is up-to-date with its corresponding + * GeoFire instance. 'ready' fires when this query has loaded all of the initial data from GeoFire and fired all + * other events for that data. It also fires every time updateCriteria() is called, after all other events have + * fired for the updated query. + * + * 'key_entered' fires when a key enters this query. This can happen when a key moves from a location outside of + * this query to one inside of it or when a key is written to GeoFire for the first time and it falls within + * this query. + * + * 'key_exited' fires when a key moves from a location inside of this query to one outside of it. If the key was + * entirely removed from GeoFire, both the location and distance passed to the callback will be null. + * + * 'key_moved' fires when a key which is already in this query moves to another location inside of it. + * + * Returns a GeoCallbackRegistration which can be used to cancel the callback. You can add as many callbacks + * as you would like for the same eventType by repeatedly calling on(). Each one will get called when its + * corresponding eventType fires. Each callback must be cancelled individually. + * + * @param eventType The event type for which to attach the callback. One of 'ready', 'key_entered', + * 'key_exited', or 'key_moved'. + * @param callback Callback function to be called when an event of type eventType fires. + * @returns A callback registration which can be used to cancel the provided callback. + */ + public on(eventType: string, callback: Function): GeoCallbackRegistration { + // Validate the inputs + if (['ready', 'key_entered', 'key_exited', 'key_moved'].indexOf(eventType) === -1) { + throw new Error('event type must be \'ready\', \'key_entered\', \'key_exited\', or \'key_moved\''); + } + if (typeof callback !== 'function') { + throw new Error('callback must be a function'); + } + + // Add the callback to this query's callbacks list + this._callbacks[eventType].push(callback); + + // If this is a 'key_entered' callback, fire it for every location already within this query + if (eventType === 'key_entered') { + const keys: string[] = Object.keys(this._locationsTracked); + keys.forEach((key: string) => { + const locationDict = this._locationsTracked[key]; + if (typeof locationDict !== 'undefined' && locationDict.isInQuery) { + callback(key, locationDict.location, locationDict.distanceFromCenter); + } + }); + } + + // If this is a 'ready' callback, fire it if this query is already ready + if (eventType === 'ready' && this._valueEventFired) { + callback(); + } + + // Return an event registration which can be used to cancel the callback + return new GeoCallbackRegistration(() => { + this._callbacks[eventType].splice(this._callbacks[eventType].indexOf(callback), 1); + }); + }; + + /** + * Returns the radius of this query, in kilometers. + * + * @returns The radius of this query, in kilometers. + */ + public radius(): number { + return this._radius; + }; + + /** + * Updates the criteria for this query. + * + * @param newQueryCriteria The criteria which specifies the query's center and radius. + */ + public updateCriteria(newQueryCriteria: QueryCriteria): void { + // Validate and save the new query criteria + validateCriteria(newQueryCriteria); + this._center = newQueryCriteria.center || this._center; + this._radius = newQueryCriteria.radius || this._radius; + + // Loop through all of the locations in the query, update their distance from the center of the + // query, and fire any appropriate events + const keys: string[] = Object.keys(this._locationsTracked); + for (const key of keys) { + // If the query was cancelled while going through this loop, stop updating locations and stop + // firing events + if (this._cancelled === true) { + break; + } + // Get the cached information for this location + const locationDict = this._locationsTracked[key]; + // Save if the location was already in the query + const wasAlreadyInQuery = locationDict.isInQuery; + // Update the location's distance to the new query center + locationDict.distanceFromCenter = GeoFire.distance(locationDict.location, this._center); + // Determine if the location is now in this query + locationDict.isInQuery = (locationDict.distanceFromCenter <= this._radius); + // If the location just left the query, fire the 'key_exited' callbacks + // Else if the location just entered the query, fire the 'key_entered' callbacks + if (wasAlreadyInQuery && !locationDict.isInQuery) { + this._fireCallbacksForKey('key_exited', key, locationDict.location, locationDict.distanceFromCenter); + } else if (!wasAlreadyInQuery && locationDict.isInQuery) { + this._fireCallbacksForKey('key_entered', key, locationDict.location, locationDict.distanceFromCenter); + } + } + + // Reset the variables which control when the 'ready' event fires + this._valueEventFired = false; + + // Listen for new geohashes being added to GeoFire and fire the appropriate events + this._listenForNewGeohashes(); + }; + + + /*********************/ + /* PRIVATE METHODS */ + /*********************/ + /** + * Turns off all callbacks for the provide geohash query. + * + * @param query The geohash query. + * @param queryState An object storing the current state of the query. + */ + private _cancelGeohashQuery(query: string[], queryState: any): void { + const queryRef = this._firebaseRef.orderByChild('g').startAt(query[0]).endAt(query[1]); + queryRef.off('child_added', queryState.childAddedCallback); + queryRef.off('child_removed', queryState.childRemovedCallback); + queryRef.off('child_changed', queryState.childChangedCallback); + queryRef.off('value', queryState.valueCallback); + } + + /** + * Callback for child added events. + * + * @param locationDataSnapshot A snapshot of the data stored for this location. + */ + private _childAddedCallback(locationDataSnapshot: firebase.database.DataSnapshot): void { + this._updateLocation(getKey(locationDataSnapshot), decodeGeoFireObject(locationDataSnapshot.val())); + } + + /** + * Callback for child changed events + * + * @param locationDataSnapshot A snapshot of the data stored for this location. + */ + private _childChangedCallback(locationDataSnapshot: firebase.database.DataSnapshot): void { + this._updateLocation(getKey(locationDataSnapshot), decodeGeoFireObject(locationDataSnapshot.val())); + } + + /** + * Callback for child removed events + * + * @param locationDataSnapshot A snapshot of the data stored for this location. + */ + private _childRemovedCallback(locationDataSnapshot: firebase.database.DataSnapshot): void { + const key: string = getKey(locationDataSnapshot); + if (this._locationsTracked.hasOwnProperty(key)) { + this._firebaseRef.child(key).once('value', (snapshot: firebase.database.DataSnapshot) => { + const location: number[] = (snapshot.val() === null) ? null : decodeGeoFireObject(snapshot.val()); + const geohash: string = (location !== null) ? encodeGeohash(location) : null; + // Only notify observers if key is not part of any other geohash query or this actually might not be + // a key exited event, but a key moved or entered event. These events will be triggered by updates + // to a different query + if (!this._geohashInSomeQuery(geohash)) { + this._removeLocation(key, location); + } + }); + } + } + + /** + * Removes unnecessary Firebase queries which are currently being queried. + */ + private _cleanUpCurrentGeohashesQueried(): void { + let keys: string[] = Object.keys(this._currentGeohashesQueried); + keys.forEach((geohashQueryStr: string) => { + const queryState: any = this._currentGeohashesQueried[geohashQueryStr]; + if (queryState.active === false) { + var query = this._stringToQuery(geohashQueryStr); + // Delete the geohash since it should no longer be queried + this._cancelGeohashQuery(query, queryState); + delete this._currentGeohashesQueried[geohashQueryStr]; + } + }); + + // Delete each location which should no longer be queried + keys = Object.keys(this._locationsTracked); + keys.forEach((key: string) => { + if (!this._geohashInSomeQuery(this._locationsTracked[key].geohash)) { + if (this._locationsTracked[key].isInQuery) { + throw new Error('Internal State error, trying to remove location that is still in query'); + } + delete this._locationsTracked[key]; + } + }); + + // Specify that this is done cleaning up the current geohashes queried + this._geohashCleanupScheduled = false; + + // Cancel any outstanding scheduled cleanup + if (this._cleanUpCurrentGeohashesQueriedTimeout !== null) { + clearTimeout(this._cleanUpCurrentGeohashesQueriedTimeout); + this._cleanUpCurrentGeohashesQueriedTimeout = null; + } + } + + /** + * Fires each callback for the provided eventType, passing it provided key's data. + * + * @param eventType The event type whose callbacks to fire. One of 'key_entered', 'key_exited', or 'key_moved'. + * @param key The key of the location for which to fire the callbacks. + * @param location The location as [latitude, longitude] pair + * @param distanceFromCenter The distance from the center or null. + */ + private _fireCallbacksForKey(eventType: string, key: string, location?: number[], distanceFromCenter?: number): void { + this._callbacks[eventType].forEach((callback) => { + if (typeof location === 'undefined' || location === null) { + callback(key, null, null); + } else { + callback(key, location, distanceFromCenter); + } + }); + } + + /** + * Fires each callback for the 'ready' event. + */ + private _fireReadyEventCallbacks(): void { + this._callbacks.ready.forEach((callback) => { + callback(); + }); + } + + /** + * Checks if this geohash is currently part of any of the geohash queries. + * + * @param geohash The geohash. + * @returns Returns true if the geohash is part of any of the current geohash queries. + */ + private _geohashInSomeQuery(geohash: string): boolean { + const keys: string[] = Object.keys(this._currentGeohashesQueried); + for (const queryStr of keys) { + if (this._currentGeohashesQueried.hasOwnProperty(queryStr)) { + var query = this._stringToQuery(queryStr); + if (geohash >= query[0] && geohash <= query[1]) { + return true; + } + } + } + + return false; + } + + /** + * Called once all geohash queries have received all child added events and fires the ready + * event if necessary. + */ + private _geohashQueryReadyCallback(queryStr?: string): void { + const index: number = this._outstandingGeohashReadyEvents.indexOf(queryStr); + if (index > -1) { + this._outstandingGeohashReadyEvents.splice(index, 1); + } + this._valueEventFired = (this._outstandingGeohashReadyEvents.length === 0); + + // If all queries have been processed, fire the ready event + if (this._valueEventFired) { + this._fireReadyEventCallbacks(); + } + } + + /** + * Attaches listeners to Firebase which track when new geohashes are added within this query's + * bounding box. + */ + private _listenForNewGeohashes(): void { + // Get the list of geohashes to query + let geohashesToQuery: string[] = geohashQueries(this._center, this._radius * 1000).map(this._queryToString); + + // Filter out duplicate geohashes + geohashesToQuery = geohashesToQuery.filter((geohash: string, i: number) => geohashesToQuery.indexOf(geohash) === i); + + // For all of the geohashes that we are already currently querying, check if they are still + // supposed to be queried. If so, don't re-query them. Otherwise, mark them to be un-queried + // next time we clean up the current geohashes queried dictionary. + const keys: string[] = Object.keys(this._currentGeohashesQueried); + keys.forEach((geohashQueryStr: string) => { + const index: number = geohashesToQuery.indexOf(geohashQueryStr); + if (index === -1) { + this._currentGeohashesQueried[geohashQueryStr].active = false; + } else { + this._currentGeohashesQueried[geohashQueryStr].active = true; + geohashesToQuery.splice(index, 1); + } + }); + + // If we are not already cleaning up the current geohashes queried and we have more than 25 of them, + // kick off a timeout to clean them up so we don't create an infinite number of unneeded queries. + if (this._geohashCleanupScheduled === false && Object.keys(this._currentGeohashesQueried).length > 25) { + this._geohashCleanupScheduled = true; + this._cleanUpCurrentGeohashesQueriedTimeout = setTimeout(this._cleanUpCurrentGeohashesQueried, 10); + } + + // Keep track of which geohashes have been processed so we know when to fire the 'ready' event + this._outstandingGeohashReadyEvents = geohashesToQuery.slice(); + + // Loop through each geohash to query for and listen for new geohashes which have the same prefix. + // For every match, attach a value callback which will fire the appropriate events. + // Once every geohash to query is processed, fire the 'ready' event. + geohashesToQuery.forEach((toQueryStr: string) => { + // decode the geohash query string + const query: string[] = this._stringToQuery(toQueryStr); + + // Create the Firebase query + const firebaseQuery: firebase.database.Query = this._firebaseRef.orderByChild('g').startAt(query[0]).endAt(query[1]); + + // For every new matching geohash, determine if we should fire the 'key_entered' event + const childAddedCallback = firebaseQuery.on('child_added', this._childAddedCallback); + const childRemovedCallback = firebaseQuery.on('child_removed', this._childRemovedCallback); + const childChangedCallback = firebaseQuery.on('child_changed', this._childChangedCallback); + + // Once the current geohash to query is processed, see if it is the last one to be processed + // and, if so, mark the value event as fired. + // Note that Firebase fires the 'value' event after every 'child_added' event fires. + const valueCallback = firebaseQuery.on('value', () => { + firebaseQuery.off('value', valueCallback); + this._geohashQueryReadyCallback(toQueryStr); + }); + + // Add the geohash query to the current geohashes queried dictionary and save its state + this._currentGeohashesQueried[toQueryStr] = { + active: true, + childAddedCallback: childAddedCallback, + childRemovedCallback: childRemovedCallback, + childChangedCallback: childChangedCallback, + valueCallback: valueCallback + }; + }); + // Based upon the algorithm to calculate geohashes, it's possible that no 'new' + // geohashes were queried even if the client updates the radius of the query. + // This results in no 'READY' event being fired after the .updateCriteria() call. + // Check to see if this is the case, and trigger the 'READY' event. + if (geohashesToQuery.length === 0) { + this._geohashQueryReadyCallback(); + } + } + + /** + * Encodes a query as a string for easier indexing and equality. + * + * @param query The query to encode. + * @returns The encoded query as string. + */ + private _queryToString(query: string[]): string { + if (query.length !== 2) { + throw new Error('Not a valid geohash query: ' + query); + } + return query[0] + ':' + query[1]; + } + + /** + * Removes the location from the local state and fires any events if necessary. + * + * @param key The key to be removed. + * @param currentLocation The current location as [latitude, longitude] pair or null if removed. + */ + private _removeLocation(key: string, currentLocation?: number[]): void { + const locationDict = this._locationsTracked[key]; + delete this._locationsTracked[key]; + if (typeof locationDict !== 'undefined' && locationDict.isInQuery) { + const distanceFromCenter: number = (currentLocation) ? GeoFire.distance(currentLocation, this._center) : null; + this._fireCallbacksForKey('key_exited', key, currentLocation, distanceFromCenter); + } + } + + /** + * Decodes a query string to a query + * + * @param str The encoded query. + * @returns The decoded query as a [start, end] pair. + */ + private _stringToQuery(str: string): string[] { + const decoded: string[] = str.split(':'); + if (decoded.length !== 2) { + throw new Error('Invalid internal state! Not a valid geohash query: ' + str); + } + return decoded; + } + + /** + * Callback for any updates to locations. Will update the information about a key and fire any necessary + * events every time the key's location changes. + * + * When a key is removed from GeoFire or the query, this function will be called with null and performs + * any necessary cleanup. + * + * @param key The key of the geofire location. + * @param location The location as [latitude, longitude] pair. + */ + private _updateLocation(key: string, location: number[]): void { + validateLocation(location); + // Get the key and location + let distanceFromCenter: number, isInQuery; + var wasInQuery: boolean = (this._locationsTracked.hasOwnProperty(key)) ? this._locationsTracked[key].isInQuery : false; + var oldLocation: number[] = (this._locationsTracked.hasOwnProperty(key)) ? this._locationsTracked[key].location : null; + + // Determine if the location is within this query + distanceFromCenter = GeoFire.distance(location, this._center); + isInQuery = (distanceFromCenter <= this._radius); + + // Add this location to the locations queried dictionary even if it is not within this query + this._locationsTracked[key] = { + location: location, + distanceFromCenter: distanceFromCenter, + isInQuery: isInQuery, + geohash: encodeGeohash(location) + }; + + // Fire the 'key_entered' event if the provided key has entered this query + if (isInQuery && !wasInQuery) { + this._fireCallbacksForKey('key_entered', key, location, distanceFromCenter); + } else if (isInQuery && oldLocation !== null && (location[0] !== oldLocation[0] || location[1] !== oldLocation[1])) { + this._fireCallbacksForKey('key_moved', key, location, distanceFromCenter); + } else if (!isInQuery && wasInQuery) { + this._fireCallbacksForKey('key_exited', key, location, distanceFromCenter); + } + } +} \ No newline at end of file diff --git a/src/geoCallbackRegistration.js b/src/geoCallbackRegistration.js deleted file mode 100644 index f8ec8407..00000000 --- a/src/geoCallbackRegistration.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Creates a GeoCallbackRegistration instance. - * - * @constructor - * @this {GeoCallbackRegistration} - * @callback cancelCallback Callback to run when this callback registration is cancelled. - */ -var GeoCallbackRegistration = function(cancelCallback) { - /********************/ - /* PUBLIC METHODS */ - /********************/ - /** - * Cancels this callback registration so that it no longer fires its callback. This - * has no effect on any other callback registrations you may have created. - */ - this.cancel = function() { - if (typeof _cancelCallback !== "undefined") { - _cancelCallback(); - _cancelCallback = undefined; - } - }; - - /*****************/ - /* CONSTRUCTOR */ - /*****************/ - if (typeof cancelCallback !== "function") { - throw new Error("callback must be a function"); - } - - var _cancelCallback = cancelCallback; -}; \ No newline at end of file diff --git a/src/geoFire.js b/src/geoFire.js deleted file mode 100644 index c6f2c4a2..00000000 --- a/src/geoFire.js +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Creates a GeoFire instance. - * - * @constructor - * @this {GeoFire} - * @param {Firebase} firebaseRef A Firebase reference where the GeoFire data will be stored. - */ -var GeoFire = function(firebaseRef) { - /********************/ - /* PUBLIC METHODS */ - /********************/ - /** - * Returns the Firebase instance used to create this GeoFire instance. - * - * @return {Firebase} The Firebase instance used to create this GeoFire instance. - */ - this.ref = function() { - return _firebaseRef; - }; - - /** - * Adds the provided key - location pair(s) to Firebase. Returns an empty promise which is fulfilled when the write is complete. - * - * If any provided key already exists in this GeoFire, it will be overwritten with the new location value. - * - * @param {string|Object} keyOrLocations The key representing the location to add or a mapping of key - location pairs which - * represent the locations to add. - * @param {Array.|undefined} location The [latitude, longitude] pair to add. - * @return {Promise.<>} A promise that is fulfilled when the write is complete. - */ - this.set = function(keyOrLocations, location) { - var locations; - if (typeof keyOrLocations === "string" && keyOrLocations.length !== 0) { - // If this is a set for a single location, convert it into a object - locations = {}; - locations[keyOrLocations] = location; - } else if (typeof keyOrLocations === "object") { - if (typeof location !== "undefined") { - throw new Error("The location argument should not be used if you pass an object to set()."); - } - locations = keyOrLocations; - } else { - throw new Error("keyOrLocations must be a string or a mapping of key - location pairs."); - } - - var newData = {}; - - Object.keys(locations).forEach(function(key) { - validateKey(key); - - var location = locations[key]; - if (location === null) { - // Setting location to null is valid since it will remove the key - newData[key] = null; - } else { - validateLocation(location); - - var geohash = encodeGeohash(location); - newData[key] = encodeGeoFireObject(location, geohash); - } - }); - - return _firebaseRef.update(newData); - }; - - /** - * Returns a promise fulfilled with the location corresponding to the provided key. - * - * If the provided key does not exist, the returned promise is fulfilled with null. - * - * @param {string} key The key of the location to retrieve. - * @return {Promise.>} A promise that is fulfilled with the location of the given key. - */ - this.get = function(key) { - validateKey(key); - return _firebaseRef.child(key).once("value").then(function(dataSnapshot) { - var snapshotVal = dataSnapshot.val(); - if (snapshotVal === null) { - return null; - } else { - return decodeGeoFireObject(snapshotVal); - } - }); - }; - - /** - * Removes the provided key from this GeoFire. Returns an empty promise fulfilled when the key has been removed. - * - * If the provided key is not in this GeoFire, the promise will still successfully resolve. - * - * @param {string} key The key of the location to remove. - * @return {Promise.} A promise that is fulfilled after the inputted key is removed. - */ - this.remove = function(key) { - return this.set(key, null); - }; - - /** - * Returns a new GeoQuery instance with the provided queryCriteria. - * - * @param {Object} queryCriteria The criteria which specifies the GeoQuery's center and radius. - * @return {GeoQuery} A new GeoQuery object. - */ - this.query = function(queryCriteria) { - return new GeoQuery(_firebaseRef, queryCriteria); - }; - - /*****************/ - /* CONSTRUCTOR */ - /*****************/ - if (Object.prototype.toString.call(firebaseRef) !== "[object Object]") { - throw new Error("firebaseRef must be an instance of Firebase"); - } - - var _firebaseRef = firebaseRef; -}; - -/** - * Static method which calculates the distance, in kilometers, between two locations, - * via the Haversine formula. Note that this is approximate due to the fact that the - * Earth's radius varies between 6356.752 km and 6378.137 km. - * - * @param {Array.} location1 The [latitude, longitude] pair of the first location. - * @param {Array.} location2 The [latitude, longitude] pair of the second location. - * @return {number} The distance, in kilometers, between the inputted locations. - */ -GeoFire.distance = function(location1, location2) { - validateLocation(location1); - validateLocation(location2); - - var radius = 6371; // Earth's radius in kilometers - var latDelta = degreesToRadians(location2[0] - location1[0]); - var lonDelta = degreesToRadians(location2[1] - location1[1]); - - var a = (Math.sin(latDelta / 2) * Math.sin(latDelta / 2)) + - (Math.cos(degreesToRadians(location1[0])) * Math.cos(degreesToRadians(location2[0])) * - Math.sin(lonDelta / 2) * Math.sin(lonDelta / 2)); - - var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - - return radius * c; -}; diff --git a/src/geoFireUtils.js b/src/geoFireUtils.js deleted file mode 100644 index 30651839..00000000 --- a/src/geoFireUtils.js +++ /dev/null @@ -1,468 +0,0 @@ -// Default geohash length -var g_GEOHASH_PRECISION = 10; - -// Characters used in location geohashes -var g_BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz"; - -// The meridional circumference of the earth in meters -var g_EARTH_MERI_CIRCUMFERENCE = 40007860; - -// Length of a degree latitude at the equator -var g_METERS_PER_DEGREE_LATITUDE = 110574; - -// Number of bits per geohash character -var g_BITS_PER_CHAR = 5; - -// Maximum length of a geohash in bits -var g_MAXIMUM_BITS_PRECISION = 22*g_BITS_PER_CHAR; - -// Equatorial radius of the earth in meters -var g_EARTH_EQ_RADIUS = 6378137.0; - -// The following value assumes a polar radius of -// var g_EARTH_POL_RADIUS = 6356752.3; -// The formulate to calculate g_E2 is -// g_E2 == (g_EARTH_EQ_RADIUS^2-g_EARTH_POL_RADIUS^2)/(g_EARTH_EQ_RADIUS^2) -// The exact value is used here to avoid rounding errors -var g_E2 = 0.00669447819799; - -// Cutoff for rounding errors on double calculations -var g_EPSILON = 1e-12; - -Math.log2 = Math.log2 || function(x) { - return Math.log(x)/Math.log(2); -}; - -/** - * Validates the inputted key and throws an error if it is invalid. - * - * @param {string} key The key to be verified. - */ -var validateKey = function(key) { - var error; - - if (typeof key !== "string") { - error = "key must be a string"; - } - else if (key.length === 0) { - error = "key cannot be the empty string"; - } - else if (1 + g_GEOHASH_PRECISION + key.length > 755) { - // Firebase can only stored child paths up to 768 characters - // The child path for this key is at the least: "i/key" - error = "key is too long to be stored in Firebase"; - } - else if (/[\[\].#$\/\u0000-\u001F\u007F]/.test(key)) { - // Firebase does not allow node keys to contain the following characters - error = "key cannot contain any of the following characters: . # $ ] [ /"; - } - - if (typeof error !== "undefined") { - throw new Error("Invalid GeoFire key '" + key + "': " + error); - } -}; - -/** - * Validates the inputted location and throws an error if it is invalid. - * - * @param {Array.} location The [latitude, longitude] pair to be verified. - */ -var validateLocation = function(location) { - var error; - - if (!Array.isArray(location)) { - error = "location must be an array"; - } - else if (location.length !== 2) { - error = "expected array of length 2, got length " + location.length; - } - else { - var latitude = location[0]; - var longitude = location[1]; - - if (typeof latitude !== "number" || isNaN(latitude)) { - error = "latitude must be a number"; - } - else if (latitude < -90 || latitude > 90) { - error = "latitude must be within the range [-90, 90]"; - } - else if (typeof longitude !== "number" || isNaN(longitude)) { - error = "longitude must be a number"; - } - else if (longitude < -180 || longitude > 180) { - error = "longitude must be within the range [-180, 180]"; - } - } - - if (typeof error !== "undefined") { - throw new Error("Invalid GeoFire location '" + location + "': " + error); - } -}; - -/** - * Validates the inputted geohash and throws an error if it is invalid. - * - * @param {string} geohash The geohash to be validated. - */ -var validateGeohash = function(geohash) { - var error; - - if (typeof geohash !== "string") { - error = "geohash must be a string"; - } - else if (geohash.length === 0) { - error = "geohash cannot be the empty string"; - } - else { - for (var i = 0, length = geohash.length; i < length; ++i) { - if (g_BASE32.indexOf(geohash[i]) === -1) { - error = "geohash cannot contain \"" + geohash[i] + "\""; - } - } - } - - if (typeof error !== "undefined") { - throw new Error("Invalid GeoFire geohash '" + geohash + "': " + error); - } -}; - -/** - * Validates the inputted query criteria and throws an error if it is invalid. - * - * @param {Object} newQueryCriteria The criteria which specifies the query's center and/or radius. - */ -var validateCriteria = function(newQueryCriteria, requireCenterAndRadius) { - if (typeof newQueryCriteria !== "object") { - throw new Error("query criteria must be an object"); - } - else if (typeof newQueryCriteria.center === "undefined" && typeof newQueryCriteria.radius === "undefined") { - throw new Error("radius and/or center must be specified"); - } - else if (requireCenterAndRadius && (typeof newQueryCriteria.center === "undefined" || typeof newQueryCriteria.radius === "undefined")) { - throw new Error("query criteria for a new query must contain both a center and a radius"); - } - - // Throw an error if there are any extraneous attributes - var keys = Object.keys(newQueryCriteria); - var numKeys = keys.length; - for (var i = 0; i < numKeys; ++i) { - var key = keys[i]; - if (key !== "center" && key !== "radius") { - throw new Error("Unexpected attribute '" + key + "'' found in query criteria"); - } - } - - // Validate the "center" attribute - if (typeof newQueryCriteria.center !== "undefined") { - validateLocation(newQueryCriteria.center); - } - - // Validate the "radius" attribute - if (typeof newQueryCriteria.radius !== "undefined") { - if (typeof newQueryCriteria.radius !== "number" || isNaN(newQueryCriteria.radius)) { - throw new Error("radius must be a number"); - } - else if (newQueryCriteria.radius < 0) { - throw new Error("radius must be greater than or equal to 0"); - } - } -}; - -/** - * Converts degrees to radians. - * - * @param {number} degrees The number of degrees to be converted to radians. - * @return {number} The number of radians equal to the inputted number of degrees. - */ -var degreesToRadians = function(degrees) { - if (typeof degrees !== "number" || isNaN(degrees)) { - throw new Error("Error: degrees must be a number"); - } - - return (degrees * Math.PI / 180); -}; - -/** - * Generates a geohash of the specified precision/string length from the [latitude, longitude] - * pair, specified as an array. - * - * @param {Array.} location The [latitude, longitude] pair to encode into a geohash. - * @param {number=} precision The length of the geohash to create. If no precision is - * specified, the global default is used. - * @return {string} The geohash of the inputted location. - */ -var encodeGeohash = function(location, precision) { - validateLocation(location); - if (typeof precision !== "undefined") { - if (typeof precision !== "number" || isNaN(precision)) { - throw new Error("precision must be a number"); - } - else if (precision <= 0) { - throw new Error("precision must be greater than 0"); - } - else if (precision > 22) { - throw new Error("precision cannot be greater than 22"); - } - else if (Math.round(precision) !== precision) { - throw new Error("precision must be an integer"); - } - } - - // Use the global precision default if no precision is specified - precision = precision || g_GEOHASH_PRECISION; - - var latitudeRange = { - min: -90, - max: 90 - }; - var longitudeRange = { - min: -180, - max: 180 - }; - var hash = ""; - var hashVal = 0; - var bits = 0; - var even = 1; - - while (hash.length < precision) { - var val = even ? location[1] : location[0]; - var range = even ? longitudeRange : latitudeRange; - var mid = (range.min + range.max) / 2; - - /* jshint -W016 */ - if (val > mid) { - hashVal = (hashVal << 1) + 1; - range.min = mid; - } - else { - hashVal = (hashVal << 1) + 0; - range.max = mid; - } - /* jshint +W016 */ - - even = !even; - if (bits < 4) { - bits++; - } - else { - bits = 0; - hash += g_BASE32[hashVal]; - hashVal = 0; - } - } - - return hash; -}; - -/** - * Calculates the number of degrees a given distance is at a given latitude. - * - * @param {number} distance The distance to convert. - * @param {number} latitude The latitude at which to calculate. - * @return {number} The number of degrees the distance corresponds to. - */ -var metersToLongitudeDegrees = function(distance, latitude) { - var radians = degreesToRadians(latitude); - var num = Math.cos(radians)*g_EARTH_EQ_RADIUS*Math.PI/180; - var denom = 1/Math.sqrt(1-g_E2*Math.sin(radians)*Math.sin(radians)); - var deltaDeg = num*denom; - if (deltaDeg < g_EPSILON) { - return distance > 0 ? 360 : 0; - } - else { - return Math.min(360, distance/deltaDeg); - } -}; - -/** - * Calculates the bits necessary to reach a given resolution, in meters, for the longitude at a - * given latitude. - * - * @param {number} resolution The desired resolution. - * @param {number} latitude The latitude used in the conversion. - * @return {number} The bits necessary to reach a given resolution, in meters. - */ -var longitudeBitsForResolution = function(resolution, latitude) { - var degs = metersToLongitudeDegrees(resolution, latitude); - return (Math.abs(degs) > 0.000001) ? Math.max(1, Math.log2(360/degs)) : 1; -}; - -/** - * Calculates the bits necessary to reach a given resolution, in meters, for the latitude. - * - * @param {number} resolution The bits necessary to reach a given resolution, in meters. - */ -var latitudeBitsForResolution = function(resolution) { - return Math.min(Math.log2(g_EARTH_MERI_CIRCUMFERENCE/2/resolution), g_MAXIMUM_BITS_PRECISION); -}; - -/** - * Wraps the longitude to [-180,180]. - * - * @param {number} longitude The longitude to wrap. - * @return {number} longitude The resulting longitude. - */ -var wrapLongitude = function(longitude) { - if (longitude <= 180 && longitude >= -180) { - return longitude; - } - var adjusted = longitude + 180; - if (adjusted > 0) { - return (adjusted % 360) - 180; - } - else { - return 180 - (-adjusted % 360); - } -}; - -/** - * Calculates the maximum number of bits of a geohash to get a bounding box that is larger than a - * given size at the given coordinate. - * - * @param {Array.} coordinate The coordinate as a [latitude, longitude] pair. - * @param {number} size The size of the bounding box. - * @return {number} The number of bits necessary for the geohash. - */ -var boundingBoxBits = function(coordinate, size) { - var latDeltaDegrees = size/g_METERS_PER_DEGREE_LATITUDE; - var latitudeNorth = Math.min(90, coordinate[0] + latDeltaDegrees); - var latitudeSouth = Math.max(-90, coordinate[0] - latDeltaDegrees); - var bitsLat = Math.floor(latitudeBitsForResolution(size))*2; - var bitsLongNorth = Math.floor(longitudeBitsForResolution(size, latitudeNorth))*2-1; - var bitsLongSouth = Math.floor(longitudeBitsForResolution(size, latitudeSouth))*2-1; - return Math.min(bitsLat, bitsLongNorth, bitsLongSouth, g_MAXIMUM_BITS_PRECISION); -}; - -/** - * Calculates eight points on the bounding box and the center of a given circle. At least one - * geohash of these nine coordinates, truncated to a precision of at most radius, are guaranteed - * to be prefixes of any geohash that lies within the circle. - * - * @param {Array.} center The center given as [latitude, longitude]. - * @param {number} radius The radius of the circle. - * @return {Array.>} The eight bounding box points. - */ -var boundingBoxCoordinates = function(center, radius) { - var latDegrees = radius/g_METERS_PER_DEGREE_LATITUDE; - var latitudeNorth = Math.min(90, center[0] + latDegrees); - var latitudeSouth = Math.max(-90, center[0] - latDegrees); - var longDegsNorth = metersToLongitudeDegrees(radius, latitudeNorth); - var longDegsSouth = metersToLongitudeDegrees(radius, latitudeSouth); - var longDegs = Math.max(longDegsNorth, longDegsSouth); - return [ - [center[0], center[1]], - [center[0], wrapLongitude(center[1] - longDegs)], - [center[0], wrapLongitude(center[1] + longDegs)], - [latitudeNorth, center[1]], - [latitudeNorth, wrapLongitude(center[1] - longDegs)], - [latitudeNorth, wrapLongitude(center[1] + longDegs)], - [latitudeSouth, center[1]], - [latitudeSouth, wrapLongitude(center[1] - longDegs)], - [latitudeSouth, wrapLongitude(center[1] + longDegs)] - ]; -}; - -/** - * Calculates the bounding box query for a geohash with x bits precision. - * - * @param {string} geohash The geohash whose bounding box query to generate. - * @param {number} bits The number of bits of precision. - * @return {Array.} A [start, end] pair of geohashes. - */ -var geohashQuery = function(geohash, bits) { - validateGeohash(geohash); - var precision = Math.ceil(bits/g_BITS_PER_CHAR); - if (geohash.length < precision) { - return [geohash, geohash+"~"]; - } - geohash = geohash.substring(0, precision); - var base = geohash.substring(0, geohash.length - 1); - var lastValue = g_BASE32.indexOf(geohash.charAt(geohash.length - 1)); - var significantBits = bits - (base.length*g_BITS_PER_CHAR); - var unusedBits = (g_BITS_PER_CHAR - significantBits); - /*jshint bitwise: false*/ - // delete unused bits - var startValue = (lastValue >> unusedBits) << unusedBits; - var endValue = startValue + (1 << unusedBits); - /*jshint bitwise: true*/ - if (endValue > 31) { - return [base+g_BASE32[startValue], base+"~"]; - } - else { - return [base+g_BASE32[startValue], base+g_BASE32[endValue]]; - } -}; - -/** - * Calculates a set of queries to fully contain a given circle. A query is a [start, end] pair - * where any geohash is guaranteed to be lexiographically larger then start and smaller than end. - * - * @param {Array.} center The center given as [latitude, longitude] pair. - * @param {number} radius The radius of the circle. - * @return {Array.>} An array of geohashes containing a [start, end] pair. - */ -var geohashQueries = function(center, radius) { - validateLocation(center); - var queryBits = Math.max(1, boundingBoxBits(center, radius)); - var geohashPrecision = Math.ceil(queryBits/g_BITS_PER_CHAR); - var coordinates = boundingBoxCoordinates(center, radius); - var queries = coordinates.map(function(coordinate) { - return geohashQuery(encodeGeohash(coordinate, geohashPrecision), queryBits); - }); - // remove duplicates - return queries.filter(function(query, index) { - return !queries.some(function(other, otherIndex) { - return index > otherIndex && query[0] === other[0] && query[1] === other[1]; - }); - }); -}; - -/** - * Encodes a location and geohash as a GeoFire object. - * - * @param {Array.} location The location as [latitude, longitude] pair. - * @param {string} geohash The geohash of the location. - * @return {Object} The location encoded as GeoFire object. - */ -function encodeGeoFireObject(location, geohash) { - validateLocation(location); - validateGeohash(geohash); - return { - ".priority": geohash, - "g": geohash, - "l": location - }; -} - -/** - * Decodes the location given as GeoFire object. Returns null if decoding fails. - * - * @param {Object} geoFireObj The location encoded as GeoFire object. - * @return {?Array.} location The location as [latitude, longitude] pair or null if - * decoding fails. - */ -function decodeGeoFireObject(geoFireObj) { - if (geoFireObj !== null && geoFireObj.hasOwnProperty("l") && Array.isArray(geoFireObj.l) && geoFireObj.l.length === 2) { - return geoFireObj.l; - } else { - throw new Error("Unexpected GeoFire location object encountered: " + JSON.stringify(geoFireObj)); - } -} - -/** - * Returns the key of a Firebase snapshot across SDK versions. - * - * @param {DataSnapshot} snapshot A Firebase snapshot. - * @return {string|null} key The Firebase snapshot's key. - */ - function getKey(snapshot) { - var key; - if (typeof snapshot.key === "function") { - key = snapshot.key(); - } else if (typeof snapshot.key === "string" || snapshot.key === null) { - key = snapshot.key; - } else { - key = snapshot.name(); - } - return key; - } diff --git a/src/geoQuery.js b/src/geoQuery.js deleted file mode 100644 index 73c4c8f1..00000000 --- a/src/geoQuery.js +++ /dev/null @@ -1,560 +0,0 @@ -/** - * Creates a GeoQuery instance. - * - * @constructor - * @this {GeoQuery} - * @param {Firebase} firebaseRef A Firebase reference. - * @param {Object} queryCriteria The criteria which specifies the query's center and radius. - */ -var GeoQuery = function (firebaseRef, queryCriteria) { - /*********************/ - /* PRIVATE METHODS */ - /*********************/ - /** - * Fires each callback for the provided eventType, passing it provided key's data. - * - * @param {string} eventType The event type whose callbacks to fire. One of "key_entered", "key_exited", or "key_moved". - * @param {string} key The key of the location for which to fire the callbacks. - * @param {?Array.} location The location as [latitude, longitude] pair - * @param {?double} distanceFromCenter The distance from the center or null. - */ - function _fireCallbacksForKey(eventType, key, location, distanceFromCenter) { - _callbacks[eventType].forEach(function(callback) { - if (typeof location === "undefined" || location === null) { - callback(key, null, null); - } - else { - callback(key, location, distanceFromCenter); - } - }); - } - - /** - * Fires each callback for the "ready" event. - */ - function _fireReadyEventCallbacks() { - _callbacks.ready.forEach(function(callback) { - callback(); - }); - } - - /** - * Decodes a query string to a query - * - * @param {string} str The encoded query. - * @return {Array.} The decoded query as a [start, end] pair. - */ - function _stringToQuery(string) { - var decoded = string.split(":"); - if (decoded.length !== 2) { - throw new Error("Invalid internal state! Not a valid geohash query: " + string); - } - return decoded; - } - - /** - * Encodes a query as a string for easier indexing and equality. - * - * @param {Array.} query The query to encode. - * @param {string} The encoded query as string. - */ - function _queryToString(query) { - if (query.length !== 2) { - throw new Error("Not a valid geohash query: " + query); - } - return query[0]+":"+query[1]; - } - - /** - * Turns off all callbacks for the provide geohash query. - * - * @param {Array.} query The geohash query. - * @param {Object} queryState An object storing the current state of the query. - */ - function _cancelGeohashQuery(query, queryState) { - var queryRef = _firebaseRef.orderByChild("g").startAt(query[0]).endAt(query[1]); - queryRef.off("child_added", queryState.childAddedCallback); - queryRef.off("child_removed", queryState.childRemovedCallback); - queryRef.off("child_changed", queryState.childChangedCallback); - queryRef.off("value", queryState.valueCallback); - } - - /** - * Removes unnecessary Firebase queries which are currently being queried. - */ - function _cleanUpCurrentGeohashesQueried() { - var keys = Object.keys(_currentGeohashesQueried); - var numKeys = keys.length; - for (var i = 0; i < numKeys; ++i) { - var geohashQueryStr = keys[i]; - var queryState = _currentGeohashesQueried[geohashQueryStr]; - if (queryState.active === false) { - var query = _stringToQuery(geohashQueryStr); - // Delete the geohash since it should no longer be queried - _cancelGeohashQuery(query, queryState); - delete _currentGeohashesQueried[geohashQueryStr]; - } - } - - // Delete each location which should no longer be queried - keys = Object.keys(_locationsTracked); - numKeys = keys.length; - for (i = 0; i < numKeys; ++i) { - var key = keys[i]; - if (!_geohashInSomeQuery(_locationsTracked[key].geohash)) { - if (_locationsTracked[key].isInQuery) { - throw new Error("Internal State error, trying to remove location that is still in query"); - } - delete _locationsTracked[key]; - } - } - - // Specify that this is done cleaning up the current geohashes queried - _geohashCleanupScheduled = false; - - // Cancel any outstanding scheduled cleanup - if (_cleanUpCurrentGeohashesQueriedTimeout !== null) { - clearTimeout(_cleanUpCurrentGeohashesQueriedTimeout); - _cleanUpCurrentGeohashesQueriedTimeout = null; - } - } - - /** - * Callback for any updates to locations. Will update the information about a key and fire any necessary - * events every time the key's location changes. - * - * When a key is removed from GeoFire or the query, this function will be called with null and performs - * any necessary cleanup. - * - * @param {string} key The key of the geofire location. - * @param {?Array.} location The location as [latitude, longitude] pair. - */ - function _updateLocation(key, location) { - validateLocation(location); - // Get the key and location - var distanceFromCenter, isInQuery; - var wasInQuery = (_locationsTracked.hasOwnProperty(key)) ? _locationsTracked[key].isInQuery : false; - var oldLocation = (_locationsTracked.hasOwnProperty(key)) ? _locationsTracked[key].location : null; - - // Determine if the location is within this query - distanceFromCenter = GeoFire.distance(location, _center); - isInQuery = (distanceFromCenter <= _radius); - - // Add this location to the locations queried dictionary even if it is not within this query - _locationsTracked[key] = { - location: location, - distanceFromCenter: distanceFromCenter, - isInQuery: isInQuery, - geohash: encodeGeohash(location, g_GEOHASH_PRECISION) - }; - - // Fire the "key_entered" event if the provided key has entered this query - if (isInQuery && !wasInQuery) { - _fireCallbacksForKey("key_entered", key, location, distanceFromCenter); - } else if (isInQuery && oldLocation !== null && (location[0] !== oldLocation[0] || location[1] !== oldLocation[1])) { - _fireCallbacksForKey("key_moved", key, location, distanceFromCenter); - } else if (!isInQuery && wasInQuery) { - _fireCallbacksForKey("key_exited", key, location, distanceFromCenter); - } - } - - /** - * Checks if this geohash is currently part of any of the geohash queries. - * - * @param {string} geohash The geohash. - * @param {boolean} Returns true if the geohash is part of any of the current geohash queries. - */ - function _geohashInSomeQuery(geohash) { - var keys = Object.keys(_currentGeohashesQueried); - var numKeys = keys.length; - for (var i = 0; i < numKeys; ++i) { - var queryStr = keys[i]; - if (_currentGeohashesQueried.hasOwnProperty(queryStr)) { - var query = _stringToQuery(queryStr); - if (geohash >= query[0] && geohash <= query[1]) { - return true; - } - } - } - return false; - } - - /** - * Removes the location from the local state and fires any events if necessary. - * - * @param {string} key The key to be removed. - * @param {?Array.} currentLocation The current location as [latitude, longitude] pair - * or null if removed. - */ - function _removeLocation(key, currentLocation) { - var locationDict = _locationsTracked[key]; - delete _locationsTracked[key]; - if (typeof locationDict !== "undefined" && locationDict.isInQuery) { - var distanceFromCenter = (currentLocation) ? GeoFire.distance(currentLocation, _center) : null; - _fireCallbacksForKey("key_exited", key, currentLocation, distanceFromCenter); - } - } - - /** - * Callback for child added events. - * - * @param {Firebase DataSnapshot} locationDataSnapshot A snapshot of the data stored for this location. - */ - function _childAddedCallback(locationDataSnapshot) { - _updateLocation(getKey(locationDataSnapshot), decodeGeoFireObject(locationDataSnapshot.val())); - } - - /** - * Callback for child changed events - * - * @param {Firebase DataSnapshot} locationDataSnapshot A snapshot of the data stored for this location. - */ - function _childChangedCallback(locationDataSnapshot) { - _updateLocation(getKey(locationDataSnapshot), decodeGeoFireObject(locationDataSnapshot.val())); - } - - /** - * Callback for child removed events - * - * @param {Firebase DataSnapshot} locationDataSnapshot A snapshot of the data stored for this location. - */ - function _childRemovedCallback(locationDataSnapshot) { - var key = getKey(locationDataSnapshot); - if (_locationsTracked.hasOwnProperty(key)) { - _firebaseRef.child(key).once("value", function(snapshot) { - var location = snapshot.val() === null ? null : decodeGeoFireObject(snapshot.val()); - var geohash = (location !== null) ? encodeGeohash(location) : null; - // Only notify observers if key is not part of any other geohash query or this actually might not be - // a key exited event, but a key moved or entered event. These events will be triggered by updates - // to a different query - if (!_geohashInSomeQuery(geohash)) { - _removeLocation(key, location); - } - }); - } - } - - /** - * Called once all geohash queries have received all child added events and fires the ready - * event if necessary. - */ - function _geohashQueryReadyCallback(queryStr) { - var index = _outstandingGeohashReadyEvents.indexOf(queryStr); - if (index > -1) { - _outstandingGeohashReadyEvents.splice(index, 1); - } - _valueEventFired = (_outstandingGeohashReadyEvents.length === 0); - - // If all queries have been processed, fire the ready event - if (_valueEventFired) { - _fireReadyEventCallbacks(); - } - } - - /** - * Attaches listeners to Firebase which track when new geohashes are added within this query's - * bounding box. - */ - function _listenForNewGeohashes() { - // Get the list of geohashes to query - var geohashesToQuery = geohashQueries(_center, _radius*1000).map(_queryToString); - - // Filter out duplicate geohashes - geohashesToQuery = geohashesToQuery.filter(function(geohash, i){ - return geohashesToQuery.indexOf(geohash) === i; - }); - - // For all of the geohashes that we are already currently querying, check if they are still - // supposed to be queried. If so, don't re-query them. Otherwise, mark them to be un-queried - // next time we clean up the current geohashes queried dictionary. - var keys = Object.keys(_currentGeohashesQueried); - var numKeys = keys.length; - for (var i = 0; i < numKeys; ++i) { - var geohashQueryStr = keys[i]; - var index = geohashesToQuery.indexOf(geohashQueryStr); - if (index === -1) { - _currentGeohashesQueried[geohashQueryStr].active = false; - } - else { - _currentGeohashesQueried[geohashQueryStr].active = true; - geohashesToQuery.splice(index, 1); - } - } - - // If we are not already cleaning up the current geohashes queried and we have more than 25 of them, - // kick off a timeout to clean them up so we don't create an infinite number of unneeded queries. - if (_geohashCleanupScheduled === false && Object.keys(_currentGeohashesQueried).length > 25) { - _geohashCleanupScheduled = true; - _cleanUpCurrentGeohashesQueriedTimeout = setTimeout(_cleanUpCurrentGeohashesQueried, 10); - } - - // Keep track of which geohashes have been processed so we know when to fire the "ready" event - _outstandingGeohashReadyEvents = geohashesToQuery.slice(); - - // Loop through each geohash to query for and listen for new geohashes which have the same prefix. - // For every match, attach a value callback which will fire the appropriate events. - // Once every geohash to query is processed, fire the "ready" event. - geohashesToQuery.forEach(function(toQueryStr) { - // decode the geohash query string - var query = _stringToQuery(toQueryStr); - - // Create the Firebase query - var firebaseQuery = _firebaseRef.orderByChild("g").startAt(query[0]).endAt(query[1]); - - // For every new matching geohash, determine if we should fire the "key_entered" event - var childAddedCallback = firebaseQuery.on("child_added", _childAddedCallback); - var childRemovedCallback = firebaseQuery.on("child_removed", _childRemovedCallback); - var childChangedCallback = firebaseQuery.on("child_changed", _childChangedCallback); - - // Once the current geohash to query is processed, see if it is the last one to be processed - // and, if so, mark the value event as fired. - // Note that Firebase fires the "value" event after every "child_added" event fires. - var valueCallback = firebaseQuery.on("value", function() { - firebaseQuery.off("value", valueCallback); - _geohashQueryReadyCallback(toQueryStr); - }); - - // Add the geohash query to the current geohashes queried dictionary and save its state - _currentGeohashesQueried[toQueryStr] = { - active: true, - childAddedCallback: childAddedCallback, - childRemovedCallback: childRemovedCallback, - childChangedCallback: childChangedCallback, - valueCallback: valueCallback - }; - }); - // Based upon the algorithm to calculate geohashes, it's possible that no "new" - // geohashes were queried even if the client updates the radius of the query. - // This results in no "READY" event being fired after the .updateCriteria() call. - // Check to see if this is the case, and trigger the "READY" event. - if(geohashesToQuery.length === 0) { - _geohashQueryReadyCallback(); - } - } - - /********************/ - /* PUBLIC METHODS */ - /********************/ - /** - * Returns the location signifying the center of this query. - * - * @return {Array.} The [latitude, longitude] pair signifying the center of this query. - */ - this.center = function() { - return _center; - }; - - /** - * Returns the radius of this query, in kilometers. - * - * @return {number} The radius of this query, in kilometers. - */ - this.radius = function() { - return _radius; - }; - - /** - * Updates the criteria for this query. - * - * @param {Object} newQueryCriteria The criteria which specifies the query's center and radius. - */ - this.updateCriteria = function(newQueryCriteria) { - // Validate and save the new query criteria - validateCriteria(newQueryCriteria); - _center = newQueryCriteria.center || _center; - _radius = newQueryCriteria.radius || _radius; - - // Loop through all of the locations in the query, update their distance from the center of the - // query, and fire any appropriate events - var keys = Object.keys(_locationsTracked); - var numKeys = keys.length; - for (var i = 0; i < numKeys; ++i) { - var key = keys[i]; - - // If the query was cancelled while going through this loop, stop updating locations and stop - // firing events - if (_cancelled === true) { - break; - } - - // Get the cached information for this location - var locationDict = _locationsTracked[key]; - - // Save if the location was already in the query - var wasAlreadyInQuery = locationDict.isInQuery; - - // Update the location's distance to the new query center - locationDict.distanceFromCenter = GeoFire.distance(locationDict.location, _center); - - // Determine if the location is now in this query - locationDict.isInQuery = (locationDict.distanceFromCenter <= _radius); - - // If the location just left the query, fire the "key_exited" callbacks - if (wasAlreadyInQuery && !locationDict.isInQuery) { - _fireCallbacksForKey("key_exited", key, locationDict.location, locationDict.distanceFromCenter); - } - - // If the location just entered the query, fire the "key_entered" callbacks - else if (!wasAlreadyInQuery && locationDict.isInQuery) { - _fireCallbacksForKey("key_entered", key, locationDict.location, locationDict.distanceFromCenter); - } - } - - // Reset the variables which control when the "ready" event fires - _valueEventFired = false; - - // Listen for new geohashes being added to GeoFire and fire the appropriate events - _listenForNewGeohashes(); - }; - - /** - * Attaches a callback to this query which will be run when the provided eventType fires. Valid eventType - * values are "ready", "key_entered", "key_exited", and "key_moved". The ready event callback is passed no - * parameters. All other callbacks will be passed three parameters: (1) the location's key, (2) the location's - * [latitude, longitude] pair, and (3) the distance, in kilometers, from the location to this query's center - * - * "ready" is used to signify that this query has loaded its initial state and is up-to-date with its corresponding - * GeoFire instance. "ready" fires when this query has loaded all of the initial data from GeoFire and fired all - * other events for that data. It also fires every time updateCriteria() is called, after all other events have - * fired for the updated query. - * - * "key_entered" fires when a key enters this query. This can happen when a key moves from a location outside of - * this query to one inside of it or when a key is written to GeoFire for the first time and it falls within - * this query. - * - * "key_exited" fires when a key moves from a location inside of this query to one outside of it. If the key was - * entirely removed from GeoFire, both the location and distance passed to the callback will be null. - * - * "key_moved" fires when a key which is already in this query moves to another location inside of it. - * - * Returns a GeoCallbackRegistration which can be used to cancel the callback. You can add as many callbacks - * as you would like for the same eventType by repeatedly calling on(). Each one will get called when its - * corresponding eventType fires. Each callback must be cancelled individually. - * - * @param {string} eventType The event type for which to attach the callback. One of "ready", "key_entered", - * "key_exited", or "key_moved". - * @callback callback Callback function to be called when an event of type eventType fires. - * @return {GeoCallbackRegistration} A callback registration which can be used to cancel the provided callback. - */ - this.on = function(eventType, callback) { - // Validate the inputs - if (["ready", "key_entered", "key_exited", "key_moved"].indexOf(eventType) === -1) { - throw new Error("event type must be \"ready\", \"key_entered\", \"key_exited\", or \"key_moved\""); - } - if (typeof callback !== "function") { - throw new Error("callback must be a function"); - } - - // Add the callback to this query's callbacks list - _callbacks[eventType].push(callback); - - // If this is a "key_entered" callback, fire it for every location already within this query - if (eventType === "key_entered") { - var keys = Object.keys(_locationsTracked); - var numKeys = keys.length; - for (var i = 0; i < numKeys; ++i) { - var key = keys[i]; - var locationDict = _locationsTracked[key]; - if (typeof locationDict !== "undefined" && locationDict.isInQuery) { - callback(key, locationDict.location, locationDict.distanceFromCenter); - } - } - } - - // If this is a "ready" callback, fire it if this query is already ready - if (eventType === "ready") { - if (_valueEventFired) { - callback(); - } - } - - // Return an event registration which can be used to cancel the callback - return new GeoCallbackRegistration(function() { - _callbacks[eventType].splice(_callbacks[eventType].indexOf(callback), 1); - }); - }; - - /** - * Terminates this query so that it no longer sends location updates. All callbacks attached to this - * query via on() will be cancelled. This query can no longer be used in the future. - */ - this.cancel = function () { - // Mark this query as cancelled - _cancelled = true; - - // Cancel all callbacks in this query's callback list - _callbacks = { - ready: [], - key_entered: [], - key_exited: [], - key_moved: [] - }; - - // Turn off all Firebase listeners for the current geohashes being queried - var keys = Object.keys(_currentGeohashesQueried); - var numKeys = keys.length; - for (var i = 0; i < numKeys; ++i) { - var geohashQueryStr = keys[i]; - var query = _stringToQuery(geohashQueryStr); - _cancelGeohashQuery(query, _currentGeohashesQueried[geohashQueryStr]); - delete _currentGeohashesQueried[geohashQueryStr]; - } - - // Delete any stored locations - _locationsTracked = {}; - - // Turn off the current geohashes queried clean up interval - clearInterval(_cleanUpCurrentGeohashesQueriedInterval); - }; - - - /*****************/ - /* CONSTRUCTOR */ - /*****************/ - // Firebase reference of the GeoFire which created this query - if (Object.prototype.toString.call(firebaseRef) !== "[object Object]") { - throw new Error("firebaseRef must be an instance of Firebase"); - } - var _firebaseRef = firebaseRef; - - // Event callbacks - var _callbacks = { - ready: [], - key_entered: [], - key_exited: [], - key_moved: [] - }; - - // Variable to track when the query is cancelled - var _cancelled = false; - - // Variables used to keep track of when to fire the "ready" event - var _valueEventFired = false; - var _outstandingGeohashReadyEvents; - - // A dictionary of locations that a currently active in the queries - // Note that not all of these are currently within this query - var _locationsTracked = {}; - - // A dictionary of geohash queries which currently have an active callbacks - var _currentGeohashesQueried = {}; - - // Every ten seconds, clean up the geohashes we are currently querying for. We keep these around - // for a little while since it's likely that they will need to be re-queried shortly after they - // move outside of the query's bounding box. - var _geohashCleanupScheduled = false; - var _cleanUpCurrentGeohashesQueriedTimeout = null; - var _cleanUpCurrentGeohashesQueriedInterval = setInterval(function() { - if (_geohashCleanupScheduled === false) { - _cleanUpCurrentGeohashesQueried(); - } - }, 10000); - - // Validate and save the query criteria - validateCriteria(queryCriteria, /* requireCenterAndRadius */ true); - var _center = queryCriteria.center; - var _radius = queryCriteria.radius; - - // Listen for new geohashes being added around this query and fire the appropriate events - _listenForNewGeohashes(); -}; diff --git a/build/header b/src/index.ts similarity index 90% rename from build/header rename to src/index.ts index 3ff8c820..48c7480c 100644 --- a/build/header +++ b/src/index.ts @@ -10,5 +10,4 @@ * License: MIT */ -var GeoFire = (function() { - "use strict"; +export { GeoFire } from './firebase/geoFire'; \ No newline at end of file diff --git a/src/interfaces/geoFireObj.ts b/src/interfaces/geoFireObj.ts new file mode 100644 index 00000000..2a387460 --- /dev/null +++ b/src/interfaces/geoFireObj.ts @@ -0,0 +1,5 @@ +export interface GeoFireObj { + '.priority': string; + g: string; + l: number[]; +} \ No newline at end of file diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts new file mode 100644 index 00000000..1c3818cc --- /dev/null +++ b/src/interfaces/index.ts @@ -0,0 +1,2 @@ +export * from './geoFireObj'; +export * from './queryCriteria'; \ No newline at end of file diff --git a/src/interfaces/queryCriteria.ts b/src/interfaces/queryCriteria.ts new file mode 100644 index 00000000..3a1b5325 --- /dev/null +++ b/src/interfaces/queryCriteria.ts @@ -0,0 +1,4 @@ +export interface QueryCriteria { + center: number[]; + radius: number; +} \ No newline at end of file diff --git a/tests/common.ts b/tests/common.ts new file mode 100644 index 00000000..151b0f3d --- /dev/null +++ b/tests/common.ts @@ -0,0 +1,128 @@ +import * as chai from 'chai'; +import * as firebase from 'firebase'; + +import { GeoFire } from '../src'; + +/*************/ +/* GLOBALS */ +/*************/ +const expect = chai.expect; +// Define examples of valid and invalid parameters +export var invalidFirebaseRefs = [null, undefined, NaN, true, false, [], 0, 5, '', 'a', ['hi', 1]]; +export var validKeys = ['a', 'loc1', '(e@Xi:4t>*E2)hc<5oa:1s6{B0d?u', Array(700).join('a')]; +export var invalidKeys = ['', true, false, null, undefined, {a: 1}, 'loc.1', 'loc$1', '[loc1', 'loc1]', 'loc#1', 'loc/1', 'a#i]$da[s', 'te/nst', 'te/rst', 'te/u0000st', 'te/u0015st', 'te/007Fst', Array(800).join('a')]; +export var validLocations = [[0, 0], [-90, 180], [90, -180], [23, 74], [47.235124363, 127.2379654226]]; +export var invalidLocations = [[-91, 0], [91, 0], [0, 181], [0, -181], [[0, 0], 0], ['a', 0], [0, 'a'], ['a', 'a'], [NaN, 0], [0, NaN], [undefined, NaN], [null, 0], [null, null], [0, undefined], [undefined, undefined], '', 'a', true, false, [], [1], {}, {a:1}, null, undefined, NaN]; +export var validGeohashes = ['4', 'd62dtu', '000000000000']; +export var invalidGeohashes = ['', 'aaa', 1, true, false, [], [1], {}, {a:1}, null, undefined, NaN]; +export var validQueryCriterias = [{center: [0,0], radius: 1000}, {center: [1,-180], radius: 1.78}, {center: [22.22,-107.77], radius: 0}, {center: [0,0]}, {center: [1,-180]}, {center: [22.22,-107.77]}, {radius: 1000}, {radius: 1.78}, {radius: 0}]; +export var invalidQueryCriterias = [{}, {random: 100}, {center: [91,2], radius: 1000, random: 'a'}, {center: [91,2], radius: 1000}, {center: [1,-181], radius: 1000}, {center: ['a',2], radius: 1000}, {center: [1,[1,2]], radius: 1000}, {center: [0,0], radius: -1}, {center: [null,2], radius: 1000}, {center: [1,undefined], radius: 1000}, {center: [NaN,0], radius: 1000}, {center: [1,2], radius: -10}, {center: [1,2], radius: 'text'}, {center: [1,2], radius: [1,2]}, {center: [1,2], radius: null}, true, false, undefined, NaN, [], 'a', 1]; + +// Create global variables to hold the Firebase and GeoFire variables +export var geoFireRef, geoFire, geoQueries = []; + +// Initialize Firebase +var config = { + apiKey: 'AIzaSyC5IcRccDo289TTRa3Y7qJIu8YPz3EnKAI', + databaseURL: 'https://geofire-9d0de.firebaseio.com' +}; +firebase.initializeApp(config); + +/**********************/ +/* HELPER FUNCTIONS */ +/**********************/ +/* Helper function which runs before each Jasmine test has started */ +export function beforeEachHelper(done) { + // Create a new Firebase database ref at a random node + geoFireRef = firebase.database().ref().push(); + + // Create a new GeoFire instance + geoFire = new GeoFire(geoFireRef); + + // Reset the GeoQueries + geoQueries = []; + + done(); +} + +/* Helper function which runs after each Jasmine test has completed */ +export function afterEachHelper(done) { + // Cancel each outstanding GeoQuery + geoQueries.forEach(function(geoQuery) { + geoQuery.cancel(); + }) + + geoFireRef.remove().then(function() { + // Wait for 50 milliseconds after each test to give enough time for old query events to expire + return wait(50); + }).then(done); +} + +/* Returns a random alphabetic string of variable length */ +export function generateRandomString() { + var possibleCharacters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var numPossibleCharacters = possibleCharacters.length; + + var text = ''; + for (var i = 0; i < 10; i++) { + text += possibleCharacters.charAt(Math.floor(Math.random() * numPossibleCharacters)); + } + + return text; +} + +/* Returns the current data in the Firebase */ +export function getFirebaseData() { + return geoFireRef.once('value').then(function(dataSnapshot) { + return dataSnapshot.exportVal(); + }); +}; + + +/* Returns a promise which is fulfilled after the inputted number of milliseconds pass */ +export function wait(milliseconds) { + return new Promise(function(resolve) { + var timeout = window.setTimeout(function() { + window.clearTimeout(timeout); + resolve(); + }, milliseconds); + }); +}; + +/* Keeps track of all the current asynchronous tasks being run */ +export function Checklist(items, expect, done) { + var eventsToComplete = items; + + /* Removes a task from the events list */ + this.x = function(item) { + var index = eventsToComplete.indexOf(item); + if (index === -1) { + expect('Attempting to delete unexpected item \'' + item + '\' from Checklist').toBeFalsy(); + } + else { + eventsToComplete.splice(index, 1); + if (this.isEmpty()) { + done(); + } + } + }; + + /* Returns the length of the events list */ + this.length = function() { + return eventsToComplete.length; + }; + + /* Returns true if the events list is empty */ + this.isEmpty = function() { + return (this.length() === 0); + }; +}; + +/* Common error handler for use in .catch() statements of promises. This will + * cause the test to fail, outputting the details of the exception. Otherwise, tests + * tend to fail due to the Jasmine ASYNC timeout and provide no details of what actually + * went wrong. + **/ +export function failTestOnCaughtError(error) { + expect(error).to.equal(null); +} diff --git a/tests/geoCallbackRegistration.test.ts b/tests/geoCallbackRegistration.test.ts new file mode 100644 index 00000000..46f139c0 --- /dev/null +++ b/tests/geoCallbackRegistration.test.ts @@ -0,0 +1,253 @@ +import { GeoCallbackRegistration } from '../src/firebase/geoCallbackRegistration'; +import { + afterEachHelper, beforeEachHelper, Checklist, + failTestOnCaughtError, geoFire, geoQueries, wait +} from './common'; + +import * as chai from 'chai'; + +const expect = chai.expect; + +describe('GeoCallbackRegistration Tests:', () => { + // Reset the Firebase before each test + beforeEach((done) => { + beforeEachHelper(done); + }); + + afterEach((done) => { + afterEachHelper(done); + }); + + describe('Constructor:', () => { + it('Constructor throws error given non-function', () => { + var createCallbackRegistration = () => { + // @ts-ignore + new GeoCallbackRegistration('nonFunction'); + } + + expect(() => createCallbackRegistration).to.throw(); + }); + }); + + describe('Cancelling event callbacks:', () => { + it('\'key_moved\' registrations can be cancelled', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 moved'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + var onKeyMovedRegistration = geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFire.set('loc1', [2, 2]); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + onKeyMovedRegistration.cancel(); + cl.x('p3'); + + return geoFire.set('loc3', [1, 2]); + }).then(() => { + cl.x('p4'); + + return wait(100); + }).then(() => { + cl.x('p5'); + }).catch(failTestOnCaughtError);; + }); + + it('\'key_entered\' registrations can be cancelled', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + var onKeyEnteredRegistration = geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [80, 80] + }).then(() => { + cl.x('p1'); + + return wait(100); + }).then(() => { + onKeyEnteredRegistration.cancel(); + cl.x('p2'); + + return geoFire.set('loc3', [1, 2]); + }).then(() => { + cl.x('p3'); + + return wait(100); + }).then(() => { + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_exited\' registrations can be cancelled', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 exited'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + var onKeyExitedRegistration = geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFire.set('loc1', [80, 80]); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + onKeyExitedRegistration.cancel(); + cl.x('p3'); + + return geoFire.set('loc3', [-80, -80]); + }).then(() => { + cl.x('p4'); + + return wait(100); + }).then(() => { + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('Cancelling a \'key_moved\' registration does not cancel all \'key_moved\' callbacks', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 moved1', 'loc1 moved2', 'loc3 moved2'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + var onKeyMovedRegistration1 = geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved1'); + }); + var onKeyMovedRegistration2 = geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved2'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFire.set('loc1', [2, 2]); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + onKeyMovedRegistration1.cancel(); + cl.x('p3'); + + return geoFire.set('loc3', [1, 2]); + }).then(() => { + cl.x('p4'); + + return wait(100); + }).then(() => { + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('Cancelling a \'key_entered\' registration does not cancel all \'key_entered\' callbacks', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered1', 'loc1 entered2', 'loc3 entered2'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + var onKeyEnteredRegistration1 = geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered1'); + }); + var onKeyEnteredRegistration2 = geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered2'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [80, 80] + }).then(() => { + cl.x('p1'); + + return wait(100); + }).then(() => { + onKeyEnteredRegistration1.cancel(); + cl.x('p2'); + + return geoFire.set('loc3', [1, 2]); + }).then(() => { + cl.x('p3'); + + return wait(100); + }).then(() => { + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('Cancelling a \'key_exited\' registration does not cancel all \'key_exited\' callbacks', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 exited1', 'loc1 exited2', 'loc3 exited2'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + var onKeyExitedRegistration1 = geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited1'); + }); + var onKeyExitedRegistration2 = geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited2'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFire.set('loc1', [80, 80]); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + onKeyExitedRegistration1.cancel(); + cl.x('p3'); + + return geoFire.set('loc3', [-80, -80]); + }).then(() => { + cl.x('p4'); + + return wait(100); + }).then(() => { + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('Calling cancel on a GeoCallbackRegistration twice does not throw', () => { + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + var onKeyExitedRegistration = geoQueries[0].on('key_exited', () => { }); + + expect(() => onKeyExitedRegistration.cancel()).not.throw(); + expect(() => onKeyExitedRegistration.cancel()).not.throw(); + }); + }); +}); diff --git a/tests/geoFire.test.ts b/tests/geoFire.test.ts new file mode 100755 index 00000000..537b4fd2 --- /dev/null +++ b/tests/geoFire.test.ts @@ -0,0 +1,842 @@ +import * as chai from 'chai'; + +import { GeoFire } from '../src/firebase/geoFire'; +import { GeoQuery } from '../src/firebase/geoQuery'; +import { + afterEachHelper, beforeEachHelper, Checklist, failTestOnCaughtError, geoFire, geoFireRef, getFirebaseData, geoQueries, + invalidFirebaseRefs, invalidKeys, invalidLocations, invalidQueryCriterias, validKeys, validLocations, validQueryCriterias +} from './common'; + +const expect = chai.expect; + +describe('GeoFire Tests:', function () { + // Reset the Firebase before each test + beforeEach(function (done) { + beforeEachHelper(done); + }); + + afterEach(function (done) { + afterEachHelper(done); + }); + + describe('Constructor:', function () { + it('Constructor throws errors given invalid Firebase references', function () { + invalidFirebaseRefs.forEach(function (invalidFirebaseRef) { + // @ts-ignore + expect(function () { new GeoFire(invalidFirebaseRef); }).to.throw(); + }); + }); + + it('Constructor does not throw errors given valid Firebase references', function () { + expect(function () { new GeoFire(geoFireRef); }).not.to.throw(); + }); + }); + + describe('ref():', function () { + it('ref() returns the Firebase reference used to create a GeoFire instance', function () { + expect(geoFire.ref()).to.deep.equal(geoFireRef); + }); + }); + + describe('Adding a single location via set():', function () { + it('set() returns a promise', function (done) { + + var cl = new Checklist(['p1'], expect, done); + + geoFire.set('loc1', [0, 0]).then(function () { + cl.x('p1'); + }); + }); + + it('set() updates Firebase when adding new locations', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); + + geoFire.set('loc1', [0, 0]).then(function () { + cl.x('p1'); + + return geoFire.set('loc2', [50, 50]); + }).then(function () { + cl.x('p2'); + + return geoFire.set('loc3', [-90, -90]); + }).then(function () { + cl.x('p3'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': { '0': 50, '1': 50 }, 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': { '0': -90, '1': -90 }, 'g': '1bpbpbpbpb' } + }); + + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('set() handles decimal latitudes and longitudes', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); + + geoFire.set('loc1', [0.254, 0]).then(function () { + cl.x('p1'); + + return geoFire.set('loc2', [50, 50.293403]); + }).then(function () { + cl.x('p2'); + + return geoFire.set('loc3', [-82.614, -90.938]); + }).then(function () { + cl.x('p3'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': 'ebpcrypzxv', 'l': { '0': 0.254, '1': 0 }, 'g': 'ebpcrypzxv' }, + 'loc2': { '.priority': 'v0gu2qnx15', 'l': { '0': 50, '1': 50.293403 }, 'g': 'v0gu2qnx15' }, + 'loc3': { '.priority': '1cr648sfx4', 'l': { '0': -82.614, '1': -90.938 }, 'g': '1cr648sfx4' } + }); + + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('set() updates Firebase when changing a pre-existing key', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + + geoFire.set('loc1', [0, 0]).then(function () { + cl.x('p1'); + + return geoFire.set('loc2', [50, 50]); + }).then(function () { + cl.x('p2'); + + return geoFire.set('loc3', [-90, -90]); + }).then(function () { + cl.x('p3'); + + return geoFire.set('loc1', [2, 3]); + }).then(function () { + cl.x('p4'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': 's065kk0dc5', 'l': { '0': 2, '1': 3 }, 'g': 's065kk0dc5' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': { '0': 50, '1': 50 }, 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': { '0': -90, '1': -90 }, 'g': '1bpbpbpbpb' } + }); + + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('set() updates Firebase when changing a pre-existing key to the same location', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + + geoFire.set('loc1', [0, 0]).then(function () { + cl.x('p1'); + + return geoFire.set('loc2', [50, 50]); + }).then(function () { + cl.x('p2'); + + return geoFire.set('loc3', [-90, -90]); + }).then(function () { + cl.x('p3'); + + return geoFire.set('loc1', [0, 0]); + }).then(function () { + cl.x('p4'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': { '0': 50, '1': 50 }, 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': { '0': -90, '1': -90 }, 'g': '1bpbpbpbpb' } + }); + + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('set() handles multiple keys at the same location', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); + + geoFire.set('loc1', [0, 0]).then(function () { + cl.x('p1'); + + return geoFire.set('loc2', [0, 0]); + }).then(function () { + cl.x('p2'); + + return geoFire.set('loc3', [0, 0]); + }).then(function () { + cl.x('p3'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' }, + 'loc3': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' } + }); + + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('set() updates Firebase after complex operations', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9', 'p10', 'p11'], expect, done); + + geoFire.set('loc:1', [0, 0]).then(function () { + cl.x('p1'); + + return geoFire.set('loc2', [50, 50]); + }).then(function () { + cl.x('p2'); + + return geoFire.set('loc%!A72f()3', [-90, -90]); + }).then(function () { + cl.x('p3'); + + return geoFire.remove('loc2'); + }).then(function () { + cl.x('p4'); + + return geoFire.set('loc2', [0.2358, -72.621]); + }).then(function () { + cl.x('p5'); + + return geoFire.set('loc4', [87.6, -130]); + }).then(function () { + cl.x('p6'); + + return geoFire.set('loc5', [5, 55.555]); + }).then(function () { + cl.x('p7'); + + return geoFire.set('loc5', null); + }).then(function () { + cl.x('p8'); + + return geoFire.set('loc:1', [87.6, -130]); + }).then(function () { + cl.x('p9'); + + return geoFire.set('loc6', [-72.258, 0.953215]); + }).then(function () { + cl.x('p10'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc:1': { '.priority': 'cped3g0fur', 'l': { '0': 87.6, '1': -130 }, 'g': 'cped3g0fur' }, + 'loc2': { '.priority': 'd2h376zj8h', 'l': { '0': 0.2358, '1': -72.621 }, 'g': 'd2h376zj8h' }, + 'loc%!A72f()3': { '.priority': '1bpbpbpbpb', 'l': { '0': -90, '1': -90 }, 'g': '1bpbpbpbpb' }, + 'loc4': { '.priority': 'cped3g0fur', 'l': { '0': 87.6, '1': -130 }, 'g': 'cped3g0fur' }, + 'loc6': { '.priority': 'h50svty4es', 'l': { '0': -72.258, '1': 0.953215 }, 'g': 'h50svty4es' } + }); + + cl.x('p11'); + }).catch(failTestOnCaughtError); + }); + + it('set() does not throw errors given valid keys', function () { + validKeys.forEach(function (validKey) { + expect(function () { + geoFire.set(validKey, [0, 0]); + }).not.to.throw(); + }); + }); + + it('set() throws errors given invalid keys', function () { + invalidKeys.forEach(function (invalidKey) { + expect(function () { + geoFire.set(invalidKey, [0, 0]); + }).to.throw(); + }); + }); + + it('set() does not throw errors given valid locations', function () { + validLocations.forEach(function (validLocation, i) { + expect(function () { + geoFire.set('loc', validLocation); + }).not.to.throw(); + }); + }); + + it('set() throws errors given invalid locations', function () { + invalidLocations.forEach(function (invalidLocation, i) { + // Setting location to null is valid since it will remove the key + if (invalidLocation !== null) { + expect(function () { + geoFire.set('loc', invalidLocation); + }).to.throw(); + } + }); + }); + }); + + describe('Adding multiple locations via set():', function () { + it('set() returns a promise', function (done) { + + var cl = new Checklist(['p1'], expect, done); + + geoFire.set({ + 'loc1': [0, 0] + }).then(function () { + cl.x('p1'); + }); + }); + + it('set() updates Firebase when adding new locations', function (done) { + var cl = new Checklist(['p1', 'p2'], expect, done); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, 50], + 'loc3': [-90, -90] + }).then(function () { + cl.x('p1'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': { '0': 50, '1': 50 }, 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': { '0': -90, '1': -90 }, 'g': '1bpbpbpbpb' } + }); + + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('set() handles decimal latitudes and longitudes', function (done) { + var cl = new Checklist(['p1', 'p2'], expect, done); + + geoFire.set({ + 'loc1': [0.254, 0], + 'loc2': [50, 50.293403], + 'loc3': [-82.614, -90.938] + }).then(function () { + cl.x('p1'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': 'ebpcrypzxv', 'l': { '0': 0.254, '1': 0 }, 'g': 'ebpcrypzxv' }, + 'loc2': { '.priority': 'v0gu2qnx15', 'l': { '0': 50, '1': 50.293403 }, 'g': 'v0gu2qnx15' }, + 'loc3': { '.priority': '1cr648sfx4', 'l': { '0': -82.614, '1': -90.938 }, 'g': '1cr648sfx4' } + }); + + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('set() updates Firebase when changing a pre-existing key', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, 50], + 'loc3': [-90, -90] + }).then(function () { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [2, 3] + }); + }).then(function () { + cl.x('p2'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': 's065kk0dc5', 'l': { '0': 2, '1': 3 }, 'g': 's065kk0dc5' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': { '0': 50, '1': 50 }, 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': { '0': -90, '1': -90 }, 'g': '1bpbpbpbpb' } + }); + + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('set() updates Firebase when changing a pre-existing key to the same location', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, 50], + 'loc3': [-90, -90] + }).then(function () { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [0, 0] + }); + }).then(function () { + cl.x('p2'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': { '0': 50, '1': 50 }, 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': { '0': -90, '1': -90 }, 'g': '1bpbpbpbpb' } + }); + + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('set() handles multiple keys at the same location', function (done) { + var cl = new Checklist(['p1', 'p2'], expect, done); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [0, 0], + 'loc3': [0, 0] + }).then(function () { + cl.x('p1'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' }, + 'loc3': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' } + }); + + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('set() updates Firebase after complex operations', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6'], expect, done); + + geoFire.set({ + 'loc:1': [0, 0], + 'loc2': [50, 50], + 'loc%!A72f()3': [-90, -90] + }).then(function () { + cl.x('p1'); + + return geoFire.remove('loc2'); + }).then(function () { + cl.x('p2'); + + return geoFire.set({ + 'loc2': [0.2358, -72.621], + 'loc4': [87.6, -130], + 'loc5': [5, 55.555] + }); + }).then(function () { + cl.x('p3'); + + return geoFire.set({ + 'loc5': null + }); + }).then(function () { + cl.x('p4'); + + return geoFire.set({ + 'loc:1': [87.6, -130], + 'loc6': [-72.258, 0.953215] + }); + }).then(function () { + cl.x('p5'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc:1': { '.priority': 'cped3g0fur', 'l': { '0': 87.6, '1': -130 }, 'g': 'cped3g0fur' }, + 'loc2': { '.priority': 'd2h376zj8h', 'l': { '0': 0.2358, '1': -72.621 }, 'g': 'd2h376zj8h' }, + 'loc%!A72f()3': { '.priority': '1bpbpbpbpb', 'l': { '0': -90, '1': -90 }, 'g': '1bpbpbpbpb' }, + 'loc4': { '.priority': 'cped3g0fur', 'l': { '0': 87.6, '1': -130 }, 'g': 'cped3g0fur' }, + 'loc6': { '.priority': 'h50svty4es', 'l': { '0': -72.258, '1': 0.953215 }, 'g': 'h50svty4es' } + }); + + cl.x('p6'); + }).catch(failTestOnCaughtError); + }); + + it('set() does not throw errors given valid keys', function () { + validKeys.forEach(function (validKey) { + expect(function () { + var locations = {}; + locations[validKey] = [0, 0]; + geoFire.set(locations); + }).not.to.throw(); + }); + }); + + it('set() throws errors given invalid keys', function () { + invalidKeys.forEach(function (invalidKey) { + if (invalidKey !== null && invalidKey !== undefined && typeof invalidKey !== 'boolean') { + expect(function () { + var locations = {}; + // @ts-ignore + locations[invalidKey] = [0, 0]; + geoFire.set(locations); + }).to.throw(); + } + }); + }); + + it('set() throws errors given a location argument in combination with an object', function () { + expect(function () { + geoFire.set({ + 'loc': [0, 0] + }, [0, 0]); + }).to.throw(); + }); + + it('set() does not throw errors given valid locations', function () { + validLocations.forEach(function (validLocation, i) { + expect(function () { + geoFire.set({ + 'loc': validLocation + }); + }).not.to.throw(); + }); + }); + + it('set() throws errors given invalid locations', function () { + invalidLocations.forEach(function (invalidLocation, i) { + // Setting location to null is valid since it will remove the key + if (invalidLocation !== null) { + expect(function () { + geoFire.set({ + 'loc': invalidLocation + }); + }).to.throw(); + } + }); + }); + }); + + describe('Retrieving locations:', function () { + it('get() returns a promise', function (done) { + var cl = new Checklist(['p1'], expect, done); + + geoFire.get('loc1').then(function () { + cl.x('p1'); + }); + }); + + it('get() returns null for non-existent keys', function (done) { + var cl = new Checklist(['p1'], expect, done); + + geoFire.get('loc1').then(function (location) { + expect(location).to.equal(null); + + cl.x('p1'); + }); + }); + + it('get() retrieves locations given existing keys', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, 50], + 'loc3': [-90, -90] + }).then(function () { + cl.x('p1'); + + return geoFire.get('loc1'); + }).then(function (location) { + expect(location).to.deep.equal([0, 0]); + cl.x('p2'); + + return geoFire.get('loc2'); + }).then(function (location) { + expect(location).to.deep.equal([50, 50]); + cl.x('p3'); + + return geoFire.get('loc3'); + }).then(function (location) { + expect(location).to.deep.equal([-90, -90]); + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('get() does not throw errors given valid keys', function () { + validKeys.forEach(function (validKey) { + expect(function () { geoFire.get(validKey); }).not.to.throw(); + }); + }); + + it('get() throws errors given invalid keys', function () { + invalidKeys.forEach(function (invalidKey) { + expect(function () { geoFire.get(invalidKey); }).to.throw(); + }); + }); + }); + + describe('Removing locations:', function () { + it('set() removes existing location given null', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [2, 3] + }).then(function () { + cl.x('p1'); + + return geoFire.get('loc1'); + }).then(function (location) { + expect(location).to.deep.equal([0, 0]); + + cl.x('p2'); + + return geoFire.set('loc1', null); + }).then(function () { + cl.x('p3'); + + return geoFire.get('loc1'); + }).then(function (location) { + expect(location).to.equal(null); + + cl.x('p4'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc2': { '.priority': 's065kk0dc5', 'l': { '0': 2, '1': 3 }, 'g': 's065kk0dc5' } + }); + + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('set() does nothing given a non-existent location and null', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + + geoFire.set('loc1', [0, 0]).then(function () { + cl.x('p1'); + + return geoFire.get('loc1'); + }).then(function (location) { + expect(location).to.deep.equal([0, 0]); + + cl.x('p2'); + + return geoFire.set('loc2', null); + }).then(function () { + cl.x('p3'); + + return geoFire.get('loc2'); + }).then(function (location) { + expect(location).to.equal(null); + + cl.x('p4'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' } + }); + + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('set() removes existing location given null', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [2, 3] + }).then(function () { + cl.x('p1'); + + return geoFire.get('loc1'); + }).then(function (location) { + expect(location).to.deep.equal([0, 0]); + + cl.x('p2'); + + return geoFire.set({ + 'loc1': null, + 'loc3': [-90, -90] + }); + }).then(function () { + cl.x('p3'); + + return geoFire.get('loc1'); + }).then(function (location) { + expect(location).to.equal(null); + + cl.x('p4'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc2': { '.priority': 's065kk0dc5', 'l': { '0': 2, '1': 3 }, 'g': 's065kk0dc5' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': { '0': -90, '1': -90 }, 'g': '1bpbpbpbpb' } + }); + + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('set() does nothing given a non-existent location and null', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': null + }).then(function () { + cl.x('p1'); + + return geoFire.get('loc1'); + }).then(function (location) { + expect(location).to.deep.equal([0, 0]); + + cl.x('p2'); + + return geoFire.get('loc2'); + }).then(function (location) { + expect(location).to.equal(null); + + cl.x('p3'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' } + }); + + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('remove() removes existing location', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + + geoFire.set({ + 'loc:^%*1': [0, 0], + 'loc2': [2, 3] + }).then(function () { + cl.x('p1'); + + return geoFire.get('loc:^%*1'); + }).then(function (location) { + expect(location).to.deep.equal([0, 0]); + + cl.x('p2'); + + return geoFire.remove('loc:^%*1'); + }).then(function () { + cl.x('p3'); + + return geoFire.get('loc:^%*1'); + }).then(function (location) { + expect(location).to.equal(null); + + cl.x('p4'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc2': { '.priority': 's065kk0dc5', 'l': { '0': 2, '1': 3 }, 'g': 's065kk0dc5' } + }); + + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('remove() does nothing given a non-existent location', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + + geoFire.set('loc1', [0, 0]).then(function () { + cl.x('p1'); + + return geoFire.get('loc1'); + }).then(function (location) { + expect(location).to.deep.equal([0, 0]); + + cl.x('p2'); + + return geoFire.remove('loc2'); + }).then(function () { + cl.x('p3'); + + return geoFire.get('loc2'); + }).then(function (location) { + expect(location).to.equal(null); + + cl.x('p4'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' } + }); + + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('remove() only removes one key if multiple keys are at the same location', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [2, 3], + 'loc3': [0, 0] + }).then(function () { + cl.x('p1'); + + return geoFire.remove('loc1'); + }).then(function () { + cl.x('p2'); + + return getFirebaseData(); + }).then(function (firebaseData) { + expect(firebaseData).to.deep.equal({ + 'loc2': { '.priority': 's065kk0dc5', 'l': { '0': 2, '1': 3 }, 'g': 's065kk0dc5' }, + 'loc3': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' } + }); + + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('remove() does not throw errors given valid keys', function () { + validKeys.forEach(function (validKey) { + expect(function () { geoFire.remove(validKey); }).not.to.throw(); + }); + }); + + it('remove() throws errors given invalid keys', function () { + invalidKeys.forEach(function (invalidKey) { + expect(function () { geoFire.remove(invalidKey); }).to.throw(); + }); + }); + }); + + describe('query():', function () { + it('query() returns GeoQuery instance', function () { + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + expect(geoQueries[0] instanceof GeoQuery).to.be.ok; + }); + + it('query() does not throw errors given valid query criteria', function () { + validQueryCriterias.forEach(function (validQueryCriteria) { + if (typeof validQueryCriteria.center !== 'undefined' && typeof validQueryCriteria.radius !== 'undefined') { + expect(function () { geoFire.query(validQueryCriteria); }).not.to.throw(); + } + }); + }); + + it('query() throws errors given invalid query criteria', function () { + invalidQueryCriterias.forEach(function (invalidQueryCriteria) { + expect(function () { geoFire.query(invalidQueryCriteria); }).to.throw(); + }); + }); + }); +}); diff --git a/tests/geoFireUtils.test.ts b/tests/geoFireUtils.test.ts new file mode 100644 index 00000000..2a8b23a0 --- /dev/null +++ b/tests/geoFireUtils.test.ts @@ -0,0 +1,296 @@ +import * as chai from 'chai'; + +import { GeoFire } from '../src/firebase/geoFire'; +import { + boundingBoxBits, degreesToRadians, encodeGeohash, geohashQuery, geohashQueries, g_GEOHASH_PRECISION, + metersToLongitudeDegrees, validateCriteria, validateGeohash, validateKey, validateLocation, wrapLongitude +} from '../src/firebase/geoFireUtils'; +import { + invalidGeohashes, invalidKeys, invalidLocations, invalidQueryCriterias, + validGeohashes, validKeys, validLocations, validQueryCriterias +} from './common'; + +const expect = chai.expect; + +describe('geoFireUtils Tests:', () => { + describe('Parameter validation:', () => { + it('validateKey() does not throw errors given valid keys', () => { + validKeys.forEach((validKey) => { + expect(() => validateKey(validKey)).not.to.throw(); + }); + }); + + it('validateKey() throws errors given invalid keys', () => { + invalidKeys.forEach((invalidKey) => { + // @ts-ignore + expect(() => validateKey(invalidKey)).to.throw(); + }); + }); + + it('validateLocation() does not throw errors given valid locations', () => { + validLocations.forEach((validLocation, i) => { + expect(() => validateLocation(validLocation)).not.to.throw(); + }); + }); + + it('validateLocation() throws errors given invalid locations', () => { + invalidLocations.forEach((invalidLocation, i) => { + // @ts-ignore + expect(() => validateLocation(invalidLocation)).to.throw(); + }); + }); + + it('validateGeohash() does not throw errors given valid geohashes', () => { + validGeohashes.forEach((validGeohash, i) => { + expect(() => validateGeohash(validGeohash)).not.to.throw(); + }); + }); + + it('validateGeohash() throws errors given invalid geohashes', () => { + invalidGeohashes.forEach((invalidGeohash, i) => { + // @ts-ignore + expect(() => validateGeohash(invalidGeohash)).to.throw(); + }); + }); + + it('validateCriteria(criteria, true) does not throw errors given valid query criteria', () => { + validQueryCriterias.forEach((validQueryCriteria) => { + if (typeof validQueryCriteria.center !== 'undefined' && typeof validQueryCriteria.radius !== 'undefined') { + expect(() => validateCriteria(validQueryCriteria, true)).not.to.throw(); + } + }); + }); + + it('validateCriteria(criteria) does not throw errors given valid query criteria', () => { + validQueryCriterias.forEach((validQueryCriteria) => { + expect(() => validateCriteria(validQueryCriteria)).not.to.throw(); + }); + }); + + it('validateCriteria(criteria, true) throws errors given invalid query criteria', () => { + invalidQueryCriterias.forEach((invalidQueryCriteria) => { + expect(() => validateCriteria(invalidQueryCriteria, true)).to.throw(); + }); + expect(() => validateCriteria({ center: [0, 0] }, true)).to.throw(); + expect(() => validateCriteria({ radius: 1000 }, true)).to.throw(); + }); + + it('validateCriteria(criteria) throws errors given invalid query criteria', () => { + invalidQueryCriterias.forEach((invalidQueryCriteria) => { + expect(() => validateCriteria(invalidQueryCriteria)).to.throw(); + }); + }); + }); + + describe('Distance calculations:', () => { + it('degreesToRadians() converts degrees to radians', () => { + expect(degreesToRadians(0)).to.be.closeTo(0, 0); + expect(degreesToRadians(45)).to.be.closeTo(0.7854, 4); + expect(degreesToRadians(90)).to.be.closeTo(1.5708, 4); + expect(degreesToRadians(135)).to.be.closeTo(2.3562, 4); + expect(degreesToRadians(180)).to.be.closeTo(3.1416, 4); + expect(degreesToRadians(225)).to.be.closeTo(3.9270, 4); + expect(degreesToRadians(270)).to.be.closeTo(4.7124, 4); + expect(degreesToRadians(315)).to.be.closeTo(5.4978, 4); + expect(degreesToRadians(360)).to.be.closeTo(6.2832, 4); + expect(degreesToRadians(-45)).to.be.closeTo(-0.7854, 4); + expect(degreesToRadians(-90)).to.be.closeTo(-1.5708, 4); + }); + + it('degreesToRadians() throws errors given invalid inputs', () => { + // @ts-ignore + expect(() => degreesToRadians('')).to.throw(); + // @ts-ignore + expect(() => degreesToRadians('a')).to.throw(); + // @ts-ignore + expect(() => degreesToRadians(true)).to.throw(); + // @ts-ignore + expect(() => degreesToRadians(false)).to.throw(); + // @ts-ignore + expect(() => degreesToRadians([1])).to.throw(); + // @ts-ignore + expect(() => degreesToRadians({})).to.throw(); + expect(() => degreesToRadians(null)).to.throw(); + expect(() => degreesToRadians(undefined)).to.throw(); + }); + + it('dist() calculates the distance between locations', () => { + expect(GeoFire.distance([90, 180], [90, 180])).to.be.closeTo(0, 0); + expect(GeoFire.distance([-90, -180], [90, 180])).to.be.closeTo(20015, 0); + expect(GeoFire.distance([-90, -180], [-90, 180])).to.be.closeTo(0, 0); + expect(GeoFire.distance([-90, -180], [90, -180])).to.be.closeTo(20015, 0); + expect(GeoFire.distance([37.7853074, -122.4054274], [78.216667, 15.55])).to.be.closeTo(6818, 0); + expect(GeoFire.distance([38.98719, -77.250783], [29.3760648, 47.9818853])).to.be.closeTo(10531, 0); + expect(GeoFire.distance([38.98719, -77.250783], [-54.933333, -67.616667])).to.be.closeTo(10484, 0); + expect(GeoFire.distance([29.3760648, 47.9818853], [-54.933333, -67.616667])).to.be.closeTo(14250, 0); + expect(GeoFire.distance([-54.933333, -67.616667], [-54, -67])).to.be.closeTo(111, 0); + }); + + it('dist() does not throw errors given valid locations', () => { + validLocations.forEach((validLocation, i) => { + expect(() => GeoFire.distance(validLocation, [0, 0])).not.to.throw(); + expect(() => GeoFire.distance([0, 0], validLocation)).not.to.throw(); + }); + }); + + it('dist() throws errors given invalid locations', () => { + invalidLocations.forEach((invalidLocation, i) => { + // @ts-ignore + expect(() => GeoFire.distance(invalidLocation, [0, 0])).to.throw(); + // @ts-ignore + expect(() => GeoFire.distance([0, 0], invalidLocation)).to.throw(); + }); + }); + }); + + describe('Geohashing:', () => { + it('encodeGeohash() encodes locations to geohashes given no precision', () => { + expect(encodeGeohash([-90, -180])).to.be('000000000000'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([90, 180])).to.be('zzzzzzzzzzzz'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([-90, 180])).to.be('pbpbpbpbpbpb'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([90, -180])).to.be('bpbpbpbpbpbp'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([37.7853074, -122.4054274])).to.be('9q8yywe56gcf'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([38.98719, -77.250783])).to.be('dqcjf17sy6cp'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([29.3760648, 47.9818853])).to.be('tj4p5gerfzqu'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([78.216667, 15.55])).to.be('umghcygjj782'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([-54.933333, -67.616667])).to.be('4qpzmren1kwb'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([-54, -67])).to.be('4w2kg3s54y7h'.slice(0, g_GEOHASH_PRECISION)); + }); + + it('encodeGeohash() encodes locations to geohashes given a custom precision', () => { + expect(encodeGeohash([-90, -180], 6)).to.be('000000'); + expect(encodeGeohash([90, 180], 20)).to.be('zzzzzzzzzzzzzzzzzzzz'); + expect(encodeGeohash([-90, 180], 1)).to.be('p'); + expect(encodeGeohash([90, -180], 5)).to.be('bpbpb'); + expect(encodeGeohash([37.7853074, -122.4054274], 8)).to.be('9q8yywe5'); + expect(encodeGeohash([38.98719, -77.250783], 18)).to.be('dqcjf17sy6cppp8vfn'); + expect(encodeGeohash([29.3760648, 47.9818853], 12)).to.be('tj4p5gerfzqu'); + expect(encodeGeohash([78.216667, 15.55], 1)).to.be('u'); + expect(encodeGeohash([-54.933333, -67.616667], 7)).to.be('4qpzmre'); + expect(encodeGeohash([-54, -67], 9)).to.be('4w2kg3s54'); + }); + + it('encodeGeohash() does not throw errors given valid locations', () => { + validLocations.forEach((validLocation, i) => { + expect(() => { encodeGeohash(validLocation); }).not.to.throw(); + }); + }); + + it('encodeGeohash() throws errors given invalid locations', () => { + invalidLocations.forEach((invalidLocation, i) => { + // @ts-ignore + expect(() => encodeGeohash(invalidLocation)).to.throw(); + }); + }); + + it('encodeGeohash() does not throw errors given valid precision', () => { + var validPrecisions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, undefined]; + + validPrecisions.forEach((validPrecision, i) => { + expect(() => encodeGeohash([0, 0], validPrecision)).not.to.throw(); + }); + }); + + it('encodeGeohash() throws errors given invalid precision', () => { + var invalidPrecisions = [0, -1, 1.5, 23, '', 'a', true, false, [], {}, [1], { a: 1 }, null]; + + invalidPrecisions.forEach((invalidPrecision, i) => { + // @ts-ignore + expect(() => encodeGeohash([0, 0], invalidPrecision)).to.throw(); + }); + }); + }); + + describe('Coordinate calculations:', () => { + it('metersToLongtitudeDegrees calculates correctly', () => { + expect(metersToLongitudeDegrees(1000, 0)).to.be.closeTo(0.008983, 5); + expect(metersToLongitudeDegrees(111320, 0)).to.be.closeTo(1, 5); + expect(metersToLongitudeDegrees(107550, 15)).to.be.closeTo(1, 5); + expect(metersToLongitudeDegrees(96486, 30)).to.be.closeTo(1, 5); + expect(metersToLongitudeDegrees(78847, 45)).to.be.closeTo(1, 5); + expect(metersToLongitudeDegrees(55800, 60)).to.be.closeTo(1, 5); + expect(metersToLongitudeDegrees(28902, 75)).to.be.closeTo(1, 5); + expect(metersToLongitudeDegrees(0, 90)).to.be.closeTo(0, 5); + expect(metersToLongitudeDegrees(1000, 90)).to.be.closeTo(360, 5); + expect(metersToLongitudeDegrees(1000, 89.9999)).to.be.closeTo(360, 5); + expect(metersToLongitudeDegrees(1000, 89.995)).to.be.closeTo(102.594208, 5); + }); + + it('wrapLongitude wraps correctly', () => { + expect(wrapLongitude(0)).to.be.closeTo(0, 6); + expect(wrapLongitude(180)).to.be.closeTo(180, 6); + expect(wrapLongitude(-180)).to.be.closeTo(-180, 6); + expect(wrapLongitude(182)).to.be.closeTo(-178, 6); + expect(wrapLongitude(270)).to.be.closeTo(-90, 6); + expect(wrapLongitude(360)).to.be.closeTo(0, 6); + expect(wrapLongitude(540)).to.be.closeTo(-180, 6); + expect(wrapLongitude(630)).to.be.closeTo(-90, 6); + expect(wrapLongitude(720)).to.be.closeTo(0, 6); + expect(wrapLongitude(810)).to.be.closeTo(90, 6); + expect(wrapLongitude(-360)).to.be.closeTo(0, 6); + expect(wrapLongitude(-182)).to.be.closeTo(178, 6); + expect(wrapLongitude(-270)).to.be.closeTo(90, 6); + expect(wrapLongitude(-360)).to.be.closeTo(0, 6); + expect(wrapLongitude(-450)).to.be.closeTo(-90, 6); + expect(wrapLongitude(-540)).to.be.closeTo(180, 6); + expect(wrapLongitude(-630)).to.be.closeTo(90, 6); + expect(wrapLongitude(1080)).to.be.closeTo(0, 6); + expect(wrapLongitude(-1080)).to.be.closeTo(0, 6); + }); + }); + + describe('Bounding box bits:', () => { + it('boundingBoxBits must return correct number of bits', () => { + expect(boundingBoxBits([35, 0], 1000)).to.be.equal(28); + expect(boundingBoxBits([35.645, 0], 1000)).to.be.equal(27); + expect(boundingBoxBits([36, 0], 1000)).to.be.equal(27); + expect(boundingBoxBits([0, 0], 1000)).to.be.equal(28); + expect(boundingBoxBits([0, -180], 1000)).to.be.equal(28); + expect(boundingBoxBits([0, 180], 1000)).to.be.equal(28); + expect(boundingBoxBits([0, 0], 8000)).to.be.equal(22); + expect(boundingBoxBits([45, 0], 1000)).to.be.equal(27); + expect(boundingBoxBits([75, 0], 1000)).to.be.equal(25); + expect(boundingBoxBits([75, 0], 2000)).to.be.equal(23); + expect(boundingBoxBits([90, 0], 1000)).to.be.equal(1); + expect(boundingBoxBits([90, 0], 2000)).to.be.equal(1); + }); + }); + + describe('Geohash queries:', () => { + it('Geohash queries must be of the right size', () => { + expect(geohashQuery('64m9yn96mx', 6)).to.equal(['60', '6h']); + expect(geohashQuery('64m9yn96mx', 1)).to.equal(['0', 'h']); + expect(geohashQuery('64m9yn96mx', 10)).to.equal(['64', '65']); + expect(geohashQuery('6409yn96mx', 11)).to.equal(['640', '64h']); + expect(geohashQuery('64m9yn96mx', 11)).to.equal(['64h', '64~']); + expect(geohashQuery('6', 10)).to.equal(['6', '6~']); + expect(geohashQuery('64z178', 12)).to.equal(['64s', '64~']); + expect(geohashQuery('64z178', 15)).to.equal(['64z', '64~']); + }); + + it('Queries from geohashQueries must contain points in circle', () => { + function inQuery(queries, hash) { + for (var i = 0; i < queries.length; i++) { + if (hash >= queries[i][0] && hash < queries[i][1]) { + return true; + } + } + return false; + } + for (var i = 0; i < 200; i++) { + var centerLat = Math.pow(Math.random(), 5) * 160 - 80; + var centerLong = Math.pow(Math.random(), 5) * 360 - 180; + var radius = Math.random() * Math.random() * 100000; + var degreeRadius = metersToLongitudeDegrees(radius, centerLat); + var queries = geohashQueries([centerLat, centerLong], radius); + for (var j = 0; j < 1000; j++) { + var pointLat = Math.max(-89.9, Math.min(89.9, centerLat + Math.random() * degreeRadius)); + var pointLong = wrapLongitude(centerLong + Math.random() * degreeRadius); + if (GeoFire.distance([centerLat, centerLong], [pointLat, pointLong]) < radius / 1000) { + expect(inQuery(queries, encodeGeohash([pointLat, pointLong]))).to.be.true; + } + } + } + }); + }); +}); diff --git a/tests/geoQuery.test.ts b/tests/geoQuery.test.ts new file mode 100644 index 00000000..44ce7c2d --- /dev/null +++ b/tests/geoQuery.test.ts @@ -0,0 +1,1412 @@ +import * as chai from 'chai'; + +import { + afterEachHelper, beforeEachHelper, Checklist, failTestOnCaughtError, geoFire, geoQueries, invalidQueryCriterias, validQueryCriterias, wait +} from './common'; + +const expect = chai.expect; + +describe('GeoQuery Tests:', () => { + // Reset the Firebase before each test + beforeEach((done) => { + beforeEachHelper(done); + }); + + afterEach((done) => { + afterEachHelper(done); + }); + + describe('Constructor:', () => { + it('Constructor stores query criteria', () => { + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + expect(geoQueries[0].center()).to.deep.equal([1, 2]); + expect(geoQueries[0].radius()).to.equal(1000); + }); + + it('Constructor throws error on invalid query criteria', () => { + expect(() => { geoFire.query({}) }).to.throw(); + expect(() => { geoFire.query({ random: 100 }) }).to.throw(); + expect(() => { geoFire.query({ center: [1, 2] }) }).to.throw(); + expect(() => { geoFire.query({ radius: 1000 }) }).to.throw(); + expect(() => { geoFire.query({ center: [91, 2], radius: 1000 }) }).to.throw(); + expect(() => { geoFire.query({ center: [1, -181], radius: 1000 }) }).to.throw(); + expect(() => { geoFire.query({ center: ['text', 2], radius: 1000 }) }).to.throw(); + expect(() => { geoFire.query({ center: [1, [1, 2]], radius: 1000 }) }).to.throw(); + expect(() => { geoFire.query({ center: 1000, radius: 1000 }) }).to.throw(); + expect(() => { geoFire.query({ center: null, radius: 1000 }) }).to.throw(); + expect(() => { geoFire.query({ center: undefined, radius: 1000 }) }).to.throw(); + expect(() => { geoFire.query({ center: [null, 2], radius: 1000 }) }).to.throw(); + expect(() => { geoFire.query({ center: [1, undefined], radius: 1000 }) }).to.throw(); + expect(() => { geoFire.query({ center: [1, 2], radius: -10 }) }).to.throw(); + expect(() => { geoFire.query({ center: [1, 2], radius: 'text' }) }).to.throw(); + expect(() => { geoFire.query({ center: [1, 2], radius: [1, 2] }) }).to.throw(); + expect(() => { geoFire.query({ center: [1, 2], radius: null }) }).to.throw(); + expect(() => { geoFire.query({ center: [1, 2], radius: undefined }) }).to.throw(); + expect(() => { geoFire.query({ center: [1, 2], radius: 1000, other: 'throw' }) }).to.throw(); + }); + }); + + describe('updateCriteria():', () => { + it('updateCriteria() updates query criteria', () => { + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + expect(geoQueries[0].center()).to.deep.equal([1, 2]); + expect(geoQueries[0].radius()).to.equal(1000); + + geoQueries[0].updateCriteria({ center: [2, 3], radius: 100 }); + + expect(geoQueries[0].center()).to.deep.equal([2, 3]); + expect(geoQueries[0].radius()).to.equal(100); + }); + + it('updateCriteria() updates query criteria when given only center', () => { + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + expect(geoQueries[0].center()).to.deep.equal([1, 2]); + expect(geoQueries[0].radius()).to.equal(1000); + + geoQueries[0].updateCriteria({ center: [2, 3] }); + + expect(geoQueries[0].center()).to.deep.equal([2, 3]); + expect(geoQueries[0].radius()).to.equal(1000); + }); + + it('updateCriteria() updates query criteria when given only radius', () => { + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + expect(geoQueries[0].center()).to.deep.equal([1, 2]); + expect(geoQueries[0].radius()).to.equal(1000); + + geoQueries[0].updateCriteria({ radius: 100 }); + + expect(geoQueries[0].center()).to.deep.equal([1, 2]); + expect(geoQueries[0].radius()).to.equal(100); + }); + + it('updateCriteria() fires \'key_entered\' callback for locations which now belong to the GeoQuery', function (done) { + var cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); + + geoQueries.push(geoFire.query({ center: [90, 90], radius: 1000 })); + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + geoQueries[0].updateCriteria({ center: [1, 2], radius: 1000 }); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('updateCriteria() fires \'key_entered\' callback for locations with complex keys which now belong to the GeoQuery', function (done) { + var cl = new Checklist(['p1', 'p2', 'loc:^:*1 entered', 'loc-+-+-4 entered'], expect, done); + + geoQueries.push(geoFire.query({ center: [90, 90], radius: 1000 })); + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + + geoFire.set({ + 'loc:^:*1': [2, 3], + 'loc:a:a:a:a:2': [50, -7], + 'loc%!@3': [16, -150], + 'loc-+-+-4': [5, 5], + 'loc:5': [67, 55] + }).then(() => { + cl.x('p1'); + + geoQueries[0].updateCriteria({ center: [1, 2], radius: 1000 }); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('updateCriteria() fires \'key_exited\' callback for locations which no longer belong to the GeoQuery', function (done) { + var cl = new Checklist(['p1', 'p2', 'loc1 exited', 'loc4 exited'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited'); + }); + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + geoQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('updateCriteria() does not cause event callbacks to fire on the previous criteria', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc1 exited', 'loc4 exited', 'loc4 entered', 'loc5 entered'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited'); + }); + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [88, 88] + }).then(() => { + cl.x('p1'); + + geoQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); + + return wait(100); + }).then(() => { + cl.x('p2'); + + return geoFire.set({ + 'loc2': [1, 1], + 'loc4': [89, 90] + }); + }).then(() => { + cl.x('p3'); + + return wait(100); + }).then(() => { + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('updateCriteria() does not cause \'key_moved\' callbacks to fire for keys in both the previous and updated queries', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc4 exited', 'loc2 entered'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited'); + }); + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved'); + }); + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [88, 88] + }).then(() => { + cl.x('p1'); + + geoQueries[0].updateCriteria({ center: [1, 1], radius: 1000 }); + + return wait(100); + }).then(() => { + cl.x('p2'); + + return geoFire.set({ + 'loc2': [1, 1], + 'loc4': [89, 90] + }); + }).then(() => { + cl.x('p3'); + + return wait(100); + }).then(() => { + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('updateCriteria() does not cause \'key_exited\' callbacks to fire twice for keys in the previous query but not in the updated query and which were moved after the query was updated', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'loc1 entered', 'loc4 entered', 'loc1 exited', 'loc4 exited', 'loc4 entered', 'loc5 entered', 'loc5 moved'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited'); + }); + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved'); + }); + + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [88, 88] + }).then(() => { + cl.x('p1'); + + geoQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); + + return wait(100); + }).then(() => { + cl.x('p2'); + + return geoFire.set({ + 'loc2': [1, 1], + 'loc4': [89, 90] + }); + }).then(() => { + cl.x('p3'); + + return wait(100); + }).then(() => { + cl.x('p4'); + + return geoFire.set({ + 'loc2': [0, 0], + 'loc5': [89, 89] + }); + }).then(() => { + cl.x('p5'); + + return wait(100); + }).then(() => { + cl.x('p6'); + }).catch(failTestOnCaughtError); + }); + + it('updateCriteria() does not throw errors given valid query criteria', () => { + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + validQueryCriterias.forEach(function (validQueryCriteria) { + expect(() => { geoQueries[0].updateCriteria(validQueryCriteria); }).not.to.throw(); + }); + }); + + it('updateCriteria() throws errors given invalid query criteria', () => { + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + invalidQueryCriterias.forEach(function (invalidQueryCriteria) { + expect(() => { geoQueries[0].updateCriteria(invalidQueryCriteria); }).to.throw(); + }); + }); + }); + + describe('on():', () => { + it('on() throws error given invalid event type', () => { + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + var setInvalidEventType = () => { + geoQueries[0].on('invalid_event', () => { }); + } + + expect(setInvalidEventType).to.throw(); + }); + + it('on() throws error given invalid callback', () => { + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + var setInvalidCallback = () => { + geoQueries[0].on('key_entered', 'non-function'); + } + + expect(setInvalidCallback).to.throw(); + }); + }); + + describe('\'ready\' event:', () => { + it('\'ready\' event fires after all \'key_entered\' events have fired', function (done) { + var cl = new Checklist(['p1', 'loc1 entered', 'loc2 entered', 'loc5 entered', 'loc6 entered', 'loc7 entered', 'loc10 entered', 'ready fired'], expect, done); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [1, 1], + 'loc3': [50, 50], + 'loc4': [14, 1], + 'loc5': [1, 2], + 'loc6': [1, 1], + 'loc7': [0, 0], + 'loc8': [-80, 44], + 'loc9': [1, -136], + 'loc10': [-2, -2] + }).then(() => { + cl.x('p1'); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + + geoQueries[0].on('ready', () => { + expect(cl.length()).to.be.equal(1); + cl.x('ready fired'); + }); + }); + }); + + it('\'ready\' event fires immediately if the callback is added after the query is already ready', function (done) { + var cl = new Checklist(['p1', 'loc1 entered', 'loc2 entered', 'loc5 entered', 'loc6 entered', 'loc7 entered', 'loc10 entered', 'ready1 fired', 'ready2 fired'], expect, done); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [1, 1], + 'loc3': [50, 50], + 'loc4': [14, 1], + 'loc5': [1, 2], + 'loc6': [1, 1], + 'loc7': [0, 0], + 'loc8': [-80, 44], + 'loc9': [1, -136], + 'loc10': [-2, -2] + }).then(() => { + cl.x('p1'); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + + geoQueries[0].on('ready', () => { + expect(cl.length()).to.be.equal(2); + cl.x('ready1 fired'); + geoQueries[0].on('ready', () => { + expect(cl.length()).to.be.equal(1); + cl.x('ready2 fired'); + }); + }); + }); + }); + + it('\'ready\' event fires after increasing the query radius, even if no new geohashes were queried', function (done) { + var cl = new Checklist(['ready1 fired', 'ready2 fired'], expect, done); + geoQueries.push(geoFire.query({ center: [37.7851382, -122.405893], radius: 6 })); + var onReadyCallbackRegistration1 = geoQueries[0].on('ready', () => { + cl.x('ready1 fired'); + onReadyCallbackRegistration1.cancel(); + geoQueries[0].updateCriteria({ + radius: 7 + }); + geoQueries[0].on('ready', () => { + cl.x('ready2 fired'); + }); + }); + }); + + it('updateCriteria() fires the \'ready\' event after all \'key_entered\' events have fired', function (done) { + var cl = new Checklist(['p1', 'loc1 entered', 'loc2 entered', 'loc5 entered', 'loc3 entered', 'loc1 exited', 'loc2 exited', 'loc5 exited', 'ready1 fired', 'ready2 fired'], expect, done); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [1, 1], + 'loc3': [50, 50], + 'loc4': [14, 1], + 'loc5': [1, 2] + }).then(() => { + cl.x('p1'); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited'); + }); + + var onReadyCallbackRegistration1 = geoQueries[0].on('ready', () => { + expect(cl.length()).to.be.equal(6); + cl.x('ready1 fired'); + + onReadyCallbackRegistration1.cancel(); + + geoQueries[0].updateCriteria({ + center: [51, 51] + }); + + geoQueries[0].on('ready', () => { + expect(cl.length()).to.be.equal(1); + cl.x('ready2 fired'); + }); + }); + }); + }); + }); + + describe('\'key_moved\' event:', () => { + it('\'key_moved\' callback does not fire for brand new locations within or outside of the GeoQuery', function (done) { + var cl = new Checklist(['p1', 'p2'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback does not fire for locations outside of the GeoQuery which are moved somewhere else outside of the GeoQuery', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved'); + }); + + geoFire.set({ + 'loc1': [1, 90], + 'loc2': [50, -7], + 'loc3': [16, -150] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [1, 91], + 'loc3': [-50, -50] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback does not fire for locations outside of the GeoQuery which are moved within the GeoQuery', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved'); + }); + + geoFire.set({ + 'loc1': [1, 90], + 'loc2': [50, -7], + 'loc3': [16, -150] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [0, 0], + 'loc3': [-1, -1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback does not fire for locations within the GeoQuery which are moved somewhere outside of the GeoQuery', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [1, 90], + 'loc3': [-1, -90] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback does not fires for a location within the GeoQuery which is set to the same location', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc3 moved'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, -1] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [0, 0], + 'loc2': [55, 55], + 'loc3': [1, 1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback fires for locations within the GeoQuery which are moved somewhere else within the GeoQuery', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [2, 2], + 'loc3': [-1, -1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback gets passed correct location parameter', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved to 2,2', 'loc3 moved to -1,-1'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved to ' + location); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [2, 2], + 'loc3': [-1, -1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback gets passed correct distance parameter', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved (111.19 km from center)', 'loc3 moved (400.90 km from center)'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved (' + distance.toFixed(2) + ' km from center)'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [2, 2], + 'loc3': [-1, -1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback properly fires when multiple keys are at the same location within the GeoQuery and only one of them moves somewhere else within the GeoQuery', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [0, 0], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [2, 2], + 'loc3': [-1, -1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback properly fires when a location within the GeoQuery moves somehwere else within the GeoQuery that is already occupied by another key', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [2, 2], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [2, 2], + 'loc3': [-1, -1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('multiple \'key_moved\' callbacks fire for locations within the GeoQuery which are moved somewhere else within the GeoQuery', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved1', 'loc3 moved1', 'loc1 moved2', 'loc3 moved2'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved1'); + }); + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved2'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [2, 2], + 'loc3': [-1, -1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + }); + + describe('\'key_entered\' event:', () => { + it('\'key_entered\' callback fires when a location enters the GeoQuery before onKeyEntered() was called', function (done) { + var cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_entered\' callback fires when a location enters the GeoQuery after onKeyEntered() was called', function (done) { + var cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_entered\' callback gets passed correct location parameter', function (done) { + var cl = new Checklist(['p1', 'p2', 'loc1 entered at 2,3', 'loc4 entered at 5,5'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered at ' + location); + }); + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_entered\' callback gets passed correct distance parameter', function (done) { + var cl = new Checklist(['p1', 'p2', 'loc1 entered (157.23 km from center)', 'loc4 entered (555.66 km from center)'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered (' + distance.toFixed(2) + ' km from center)'); + }); + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_entered\' callback properly fires when multiple keys are at the same location outside the GeoQuery and only one of them moves within the GeoQuery', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 entered'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + + geoFire.set({ + 'loc1': [50, 50], + 'loc2': [50, 50], + 'loc3': [18, -121] + }).then(() => { + cl.x('p1'); + + return geoFire.set('loc1', [2, 2]); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_entered\' callback properly fires when a location outside the GeoQuery moves somewhere within the GeoQuery that is already occupied by another key', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 entered', 'loc3 entered'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + + geoFire.set({ + 'loc1': [50, 50], + 'loc2': [50, 50], + 'loc3': [0, 0] + }).then(() => { + cl.x('p1'); + + return geoFire.set('loc1', [0, 0]); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('multiple \'key_entered\' callbacks fire when a location enters the GeoQuery', function (done) { + var cl = new Checklist(['p1', 'p2', 'loc1 entered1', 'loc4 entered1', 'loc1 entered2', 'loc4 entered2'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered1'); + }); + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered2'); + }); + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + }); + + describe('\'key_exited\' event:', () => { + it('\'key_exited\' callback fires when a location leaves the GeoQuery', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited', 'loc4 exited'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited'); + }); + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [25, 90], + 'loc4': [25, 5] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_exited\' callback gets passed correct location parameter', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited to 25,90', 'loc4 exited to 25,5'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited to ' + location); + }); + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [5, 2], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [25, 90], + 'loc2': [5, 5], + 'loc4': [25, 5] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_exited\' callback gets passed correct distance parameter', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited (9759.01 km from center)', 'loc4 exited (2688.06 km from center)'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited (' + distance.toFixed(2) + ' km from center)'); + }); + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [5, 2], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [25, 90], + 'loc2': [5, 5], + 'loc4': [25, 5] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_exited\' callback gets passed null for location and distance parameters if the key is entirely removed from GeoFire', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_exited', function (key, location, distance) { + expect(location).to.be.equal(null); + expect(distance).to.be.equal(null); + cl.x(key + ' exited'); + }); + + geoFire.set('loc1', [2, 3]).then(() => { + cl.x('p1'); + + return geoFire.remove('loc1'); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_exited\' callback fires when a location within the GeoQuery is entirely removed from GeoFire', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [2, 3] + }).then(() => { + cl.x('p1'); + + return geoFire.remove('loc1'); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_exited\' callback properly fires when multiple keys are at the same location inside the GeoQuery and only one of them moves outside the GeoQuery', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [0, 0], + 'loc3': [18, -121] + }).then(() => { + cl.x('p1'); + + return geoFire.set('loc1', [20, -55]); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_exited\' callback properly fires when a location inside the GeoQuery moves somewhere outside the GeoQuery that is already occupied by another key', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited'); + }); + + geoFire.set({ + 'loc1': [0, 0], + 'loc2': [50, 50], + 'loc3': [18, -121] + }).then(() => { + cl.x('p1'); + + return geoFire.set('loc1', [18, -121]); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('multiple \'key_exited\' callbacks fire when a location leaves the GeoQuery', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited1', 'loc4 exited1', 'loc1 exited2', 'loc4 exited2'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited1'); + }); + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited2'); + }); + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [25, 90], + 'loc4': [25, 5] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + }); + + describe('\'key_*\' events combined:', () => { + it('\'key_*\' event callbacks fire when used all at the same time', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc1 moved', 'loc4 exited', 'loc1 exited', 'loc5 entered'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited'); + }); + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved'); + }); + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [1, 1], + 'loc4': [25, 5] + }); + }).then(() => { + cl.x('p2'); + + return geoFire.set({ + 'loc1': [10, -100], + 'loc2': [50, -50], + 'loc5': [5, 5] + }); + }).then(() => { + cl.x('p3'); + + return wait(100); + }).then(() => { + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('location moving between geohash queries triggers a key_moved', function (done) { + var cl = new Checklist(['loc1 entered', 'loc2 entered', 'p1', 'loc1 moved', 'loc2 moved', 'p2'], expect, done); + + geoQueries.push(geoFire.query({ center: [0, 0], radius: 1000 })); + + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited'); + }); + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved'); + }); + + geoFire.set({ + 'loc1': [-1, -1], + 'loc2': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [1, 1], + 'loc2': [-1, -1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).catch(failTestOnCaughtError); + }); + }); + + describe('Cancelling GeoQuery:', () => { + it('cancel() prevents GeoQuery from firing any more \'key_*\' event callbacks', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 entered', 'loc4 entered', 'loc1 moved', 'loc4 exited'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited'); + }); + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved'); + }); + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [1, 1], + 'loc4': [25, 5] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3') + + geoQueries[0].cancel(); + + return wait(1000); + }).then(() => { + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered'); + }); + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited'); + }); + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved'); + }); + + return geoFire.set({ + 'loc1': [10, -100], + 'loc2': [50, -50], + 'loc5': [5, 5] + }); + }).then(() => { + cl.x('p4'); + + return wait(100); + }).then(() => { + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('Calling cancel() on one GeoQuery does not cancel other GeoQueries', function (done) { + var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 entered1', 'loc1 entered2', 'loc4 entered1', 'loc4 entered2', 'loc1 moved1', 'loc1 moved2', 'loc4 exited1', 'loc4 exited2', 'loc1 exited2', 'loc5 entered2'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered1'); + }); + geoQueries[0].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited1'); + }); + geoQueries[0].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved1'); + }); + + geoQueries[1].on('key_entered', function (key, location, distance) { + cl.x(key + ' entered2'); + }); + geoQueries[1].on('key_exited', function (key, location, distance) { + cl.x(key + ' exited2'); + }); + geoQueries[1].on('key_moved', function (key, location, distance) { + cl.x(key + ' moved2'); + }); + + geoFire.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return geoFire.set({ + 'loc1': [1, 1], + 'loc4': [25, 5] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3') + + geoQueries[0].cancel(); + + return geoFire.set({ + 'loc1': [10, -100], + 'loc2': [50, -50], + 'loc5': [1, 2] + }); + }).then(() => { + cl.x('p4'); + + return wait(100); + }).then(() => { + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('Calling cancel() in the middle of firing \'key_entered\' events is allowed', function (done) { + var cl = new Checklist(['p1', 'key entered', 'cancel query'], expect, done); + + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + + geoFire.set({ + 'loc1': [1, 2], + 'loc2': [1, 3], + 'loc3': [1, 4] + }).then(() => { + cl.x('p1'); + + var numKeyEnteredEventsFired = 0; + geoQueries[0].on('key_entered', function (key, location, distance) { + cl.x('key entered'); + numKeyEnteredEventsFired++; + if (numKeyEnteredEventsFired === 1) { + cl.x('cancel query'); + geoQueries[0].cancel(); + } + }); + }).catch(failTestOnCaughtError); + }); + }); +}); diff --git a/tests/index.html b/tests/index.html deleted file mode 100755 index d1c0f1d9..00000000 --- a/tests/index.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - GeoFire Test Suite - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/karma.conf.js b/tests/karma.conf.js deleted file mode 100644 index 06c7f3c0..00000000 --- a/tests/karma.conf.js +++ /dev/null @@ -1,24 +0,0 @@ -// Configuration file for Karma test runner -module.exports = function(config) { - config.set({ - frameworks: ["jasmine"], - preprocessors: { - "../src/*.js": "coverage" - }, - reporters: ["spec", "failed", "coverage"], - coverageReporter: { - reporters: [ - { - type: "lcovonly", - dir: "coverage", - subdir: "." - }, - { - type: "text-summary" - } - ] - }, - browsers: ["Firefox"], - browserNoActivityTimeout: 30000 - }); -}; diff --git a/tests/specs/common.spec.js b/tests/specs/common.spec.js deleted file mode 100644 index dd10dc6e..00000000 --- a/tests/specs/common.spec.js +++ /dev/null @@ -1,125 +0,0 @@ -/*************/ -/* GLOBALS */ -/*************/ -// Override the default timeout interval for Jasmine -jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; - -// Define examples of valid and invalid parameters -var invalidFirebaseRefs = [null, undefined, NaN, true, false, [], 0, 5, "", "a", ["hi", 1]]; -var validKeys = ["a", "loc1", "(e@Xi:4t>*E2)hc<5oa:1s6{B0d?u", Array(700).join("a")]; -var invalidKeys = ["", true, false, null, undefined, {a: 1}, "loc.1", "loc$1", "[loc1", "loc1]", "loc#1", "loc/1", "a#i]$da[s", "te/nst", "te/rst", "te/u0000st", "te/u0015st", "te/007Fst", Array(800).join("a")]; -var validLocations = [[0, 0], [-90, 180], [90, -180], [23, 74], [47.235124363, 127.2379654226]]; -var invalidLocations = [[-91, 0], [91, 0], [0, 181], [0, -181], [[0, 0], 0], ["a", 0], [0, "a"], ["a", "a"], [NaN, 0], [0, NaN], [undefined, NaN], [null, 0], [null, null], [0, undefined], [undefined, undefined], "", "a", true, false, [], [1], {}, {a:1}, null, undefined, NaN]; -var validGeohashes = ["4", "d62dtu", "000000000000"]; -var invalidGeohashes = ["", "aaa", 1, true, false, [], [1], {}, {a:1}, null, undefined, NaN]; -var validQueryCriterias = [{center: [0,0], radius: 1000}, {center: [1,-180], radius: 1.78}, {center: [22.22,-107.77], radius: 0}, {center: [0,0]}, {center: [1,-180]}, {center: [22.22,-107.77]}, {radius: 1000}, {radius: 1.78}, {radius: 0}]; -var invalidQueryCriterias = [{}, {random: 100}, {center: [91,2], radius: 1000, random: "a"}, {center: [91,2], radius: 1000}, {center: [1,-181], radius: 1000}, {center: ["a",2], radius: 1000}, {center: [1,[1,2]], radius: 1000}, {center: [0,0], radius: -1}, {center: [null,2], radius: 1000}, {center: [1,undefined], radius: 1000}, {center: [NaN,0], radius: 1000}, {center: [1,2], radius: -10}, {center: [1,2], radius: "text"}, {center: [1,2], radius: [1,2]}, {center: [1,2], radius: null}, true, false, undefined, NaN, [], "a", 1]; - -// Create global variables to hold the Firebase and GeoFire variables -var geoFireRef, geoFire, geoQueries = []; - -// Initialize Firebase -var config = { - apiKey: "AIzaSyC5IcRccDo289TTRa3Y7qJIu8YPz3EnKAI", - databaseURL: "https://geofire-9d0de.firebaseio.com" -}; -firebase.initializeApp(config); - -/**********************/ -/* HELPER FUNCTIONS */ -/**********************/ -/* Helper function which runs before each Jasmine test has started */ -function beforeEachHelper(done) { - // Create a new Firebase database ref at a random node - geoFireRef = firebase.database().ref().push(); - - // Create a new GeoFire instance - geoFire = new GeoFire(geoFireRef); - - // Reset the GeoQueries - geoQueries = []; - - done(); -} - -/* Helper function which runs after each Jasmine test has completed */ -function afterEachHelper(done) { - // Cancel each outstanding GeoQuery - geoQueries.forEach(function(geoQuery) { - geoQuery.cancel(); - }) - - geoFireRef.remove().then(function() { - // Wait for 50 milliseconds after each test to give enough time for old query events to expire - return wait(50); - }).then(done); -} - -/* Returns a random alphabetic string of variable length */ -function generateRandomString() { - var possibleCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - var numPossibleCharacters = possibleCharacters.length; - - var text = ""; - for (var i = 0; i < 10; i++) { - text += possibleCharacters.charAt(Math.floor(Math.random() * numPossibleCharacters)); - } - - return text; -} - -/* Returns the current data in the Firebase */ -function getFirebaseData() { - return geoFireRef.once("value").then(function(dataSnapshot) { - return dataSnapshot.exportVal(); - }); -}; - - -/* Returns a promise which is fulfilled after the inputted number of milliseconds pass */ -function wait(milliseconds) { - return new RSVP.Promise(function(resolve) { - var timeout = window.setTimeout(function() { - window.clearTimeout(timeout); - resolve(); - }, milliseconds); - }); -}; - -/* Keeps track of all the current asynchronous tasks being run */ -function Checklist(items, expect, done) { - var eventsToComplete = items; - - /* Removes a task from the events list */ - this.x = function(item) { - var index = eventsToComplete.indexOf(item); - if (index === -1) { - expect("Attempting to delete unexpected item '" + item + "' from Checklist").toBeFalsy(); - } - else { - eventsToComplete.splice(index, 1); - if (this.isEmpty()) { - done(); - } - } - }; - - /* Returns the length of the events list */ - this.length = function() { - return eventsToComplete.length; - }; - - /* Returns true if the events list is empty */ - this.isEmpty = function() { - return (this.length() === 0); - }; -}; - -/* Common error handler for use in .catch() statements of promises. This will - * cause the test to fail, outputting the details of the exception. Otherwise, tests - * tend to fail due to the Jasmine ASYNC timeout and provide no details of what actually - * went wrong. - **/ -function failTestOnCaughtError(error) { - expect(error).toBeNull(); -} diff --git a/tests/specs/geoCallbackRegistration.spec.js b/tests/specs/geoCallbackRegistration.spec.js deleted file mode 100644 index e3fe31fd..00000000 --- a/tests/specs/geoCallbackRegistration.spec.js +++ /dev/null @@ -1,242 +0,0 @@ -describe("GeoCallbackRegistration Tests:", function() { - // Reset the Firebase before each test - beforeEach(function(done) { - beforeEachHelper(done); - }); - - afterEach(function(done) { - afterEachHelper(done); - }); - - describe("Constructor:", function() { - it("Constructor throws error given non-function", function() { - var createCallbackRegistration = function() { - GeoCallbackRegistration("nonFunction"); - } - - expect(createCallbackRegistration).toThrow(); - }); - }); - - describe("Cancelling event callbacks:", function() { - it("\"key_moved\" registrations can be cancelled", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "p5", "loc1 moved"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - var onKeyMovedRegistration = geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, -7], - "loc3": [1, 1] - }).then(function() { - cl.x("p1"); - - return geoFire.set("loc1", [2, 2]); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - onKeyMovedRegistration.cancel(); - cl.x("p3"); - - return geoFire.set("loc3", [1, 2]); - }).then(function() { - cl.x("p4"); - - return wait(100); - }).then(function() { - cl.x("p5"); - }).catch(failTestOnCaughtError);; - }); - - it("\"key_entered\" registrations can be cancelled", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "loc1 entered"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - var onKeyEnteredRegistration = geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, -7], - "loc3": [80, 80] - }).then(function() { - cl.x("p1"); - - return wait(100); - }).then(function() { - onKeyEnteredRegistration.cancel(); - cl.x("p2"); - - return geoFire.set("loc3", [1, 2]); - }).then(function() { - cl.x("p3"); - - return wait(100); - }).then(function() { - cl.x("p4"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_exited\" registrations can be cancelled", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "p5", "loc1 exited"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - var onKeyExitedRegistration = geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, -7], - "loc3": [1, 1] - }).then(function() { - cl.x("p1"); - - return geoFire.set("loc1", [80, 80]); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - onKeyExitedRegistration.cancel(); - cl.x("p3"); - - return geoFire.set("loc3", [-80, -80]); - }).then(function() { - cl.x("p4"); - - return wait(100); - }).then(function() { - cl.x("p5"); - }).catch(failTestOnCaughtError); - }); - - it("Cancelling a \"key_moved\" registration does not cancel all \"key_moved\" callbacks", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "p5", "loc1 moved1", "loc1 moved2", "loc3 moved2"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - var onKeyMovedRegistration1 = geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved1"); - }); - var onKeyMovedRegistration2 = geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved2"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, -7], - "loc3": [1, 1] - }).then(function() { - cl.x("p1"); - - return geoFire.set("loc1", [2, 2]); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - onKeyMovedRegistration1.cancel(); - cl.x("p3"); - - return geoFire.set("loc3", [1, 2]); - }).then(function() { - cl.x("p4"); - - return wait(100); - }).then(function() { - cl.x("p5"); - }).catch(failTestOnCaughtError); - }); - - it("Cancelling a \"key_entered\" registration does not cancel all \"key_entered\" callbacks", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "loc1 entered1", "loc1 entered2", "loc3 entered2"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - var onKeyEnteredRegistration1 = geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered1"); - }); - var onKeyEnteredRegistration2 = geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered2"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, -7], - "loc3": [80, 80] - }).then(function() { - cl.x("p1"); - - return wait(100); - }).then(function() { - onKeyEnteredRegistration1.cancel(); - cl.x("p2"); - - return geoFire.set("loc3", [1, 2]); - }).then(function() { - cl.x("p3"); - - return wait(100); - }).then(function() { - cl.x("p4"); - }).catch(failTestOnCaughtError); - }); - - it("Cancelling a \"key_exited\" registration does not cancel all \"key_exited\" callbacks", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "p5", "loc1 exited1", "loc1 exited2", "loc3 exited2"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - var onKeyExitedRegistration1 = geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited1"); - }); - var onKeyExitedRegistration2 = geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited2"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, -7], - "loc3": [1, 1] - }).then(function() { - cl.x("p1"); - - return geoFire.set("loc1", [80, 80]); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - onKeyExitedRegistration1.cancel(); - cl.x("p3"); - - return geoFire.set("loc3", [-80, -80]); - }).then(function() { - cl.x("p4"); - - return wait(100); - }).then(function() { - cl.x("p5"); - }).catch(failTestOnCaughtError); - }); - - it("Calling cancel on a GeoCallbackRegistration twice does not throw", function() { - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - var onKeyExitedRegistration = geoQueries[0].on("key_exited", function() {}); - - expect(function() { onKeyExitedRegistration.cancel() }).not.toThrow(); - expect(function() { onKeyExitedRegistration.cancel() }).not.toThrow(); - }); - }); -}); diff --git a/tests/specs/geoFire.spec.js b/tests/specs/geoFire.spec.js deleted file mode 100755 index f98bcc26..00000000 --- a/tests/specs/geoFire.spec.js +++ /dev/null @@ -1,829 +0,0 @@ -describe("GeoFire Tests:", function() { - // Reset the Firebase before each test - beforeEach(function(done) { - beforeEachHelper(done); - }); - - afterEach(function(done) { - afterEachHelper(done); - }); - - describe("Constructor:", function() { - it("Constructor throws errors given invalid Firebase references", function() { - invalidFirebaseRefs.forEach(function(invalidFirebaseRef) { - expect(function() { new GeoFire(invalidFirebaseRef); }).toThrow(); - }); - }); - - it("Constructor does not throw errors given valid Firebase references", function() { - expect(function() { new GeoFire(geoFireRef); }).not.toThrow(); - }); - }); - - describe("ref():", function() { - it("ref() returns the Firebase reference used to create a GeoFire instance", function() { - expect(geoFire.ref()).toBe(geoFireRef); - }); - }); - - describe("Adding a single location via set():", function() { - it("set() returns a promise", function(done) { - - var cl = new Checklist(["p1"], expect, done); - - geoFire.set("loc1", [0, 0]).then(function() { - cl.x("p1"); - }); - }); - - it("set() updates Firebase when adding new locations", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4"], expect, done); - - geoFire.set("loc1", [0, 0]).then(function() { - cl.x("p1"); - - return geoFire.set("loc2", [50, 50]); - }).then(function() { - cl.x("p2"); - - return geoFire.set("loc3", [-90, -90]); - }).then(function() { - cl.x("p3"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc1": { ".priority": "7zzzzzzzzz", "l": { "0": 0, "1": 0 }, "g": "7zzzzzzzzz" }, - "loc2": { ".priority": "v0gs3y0zh7", "l": { "0": 50, "1": 50 }, "g": "v0gs3y0zh7" }, - "loc3": { ".priority": "1bpbpbpbpb", "l": { "0": -90, "1": -90 }, "g": "1bpbpbpbpb" } - }); - - cl.x("p4"); - }).catch(failTestOnCaughtError); - }); - - it("set() handles decimal latitudes and longitudes", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4"], expect, done); - - geoFire.set("loc1", [0.254, 0]).then(function() { - cl.x("p1"); - - return geoFire.set("loc2", [50, 50.293403]); - }).then(function() { - cl.x("p2"); - - return geoFire.set("loc3", [-82.614, -90.938]); - }).then(function() { - cl.x("p3"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc1": { ".priority": "ebpcrypzxv", "l": { "0": 0.254, "1": 0 }, "g": "ebpcrypzxv" }, - "loc2": { ".priority": "v0gu2qnx15", "l": { "0": 50, "1": 50.293403 }, "g": "v0gu2qnx15" }, - "loc3": { ".priority": "1cr648sfx4", "l": { "0": -82.614, "1": -90.938 }, "g": "1cr648sfx4" } - }); - - cl.x("p4"); - }).catch(failTestOnCaughtError); - }); - - it("set() updates Firebase when changing a pre-existing key", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "p5"], expect, done); - - geoFire.set("loc1", [0, 0]).then(function() { - cl.x("p1"); - - return geoFire.set("loc2", [50, 50]); - }).then(function() { - cl.x("p2"); - - return geoFire.set("loc3", [-90, -90]); - }).then(function() { - cl.x("p3"); - - return geoFire.set("loc1", [2, 3]); - }).then(function() { - cl.x("p4"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc1": { ".priority": "s065kk0dc5", "l": { "0": 2, "1": 3 }, "g": "s065kk0dc5" }, - "loc2": { ".priority": "v0gs3y0zh7", "l": { "0": 50, "1": 50 }, "g": "v0gs3y0zh7" }, - "loc3": { ".priority": "1bpbpbpbpb", "l": { "0": -90, "1": -90 }, "g": "1bpbpbpbpb" } - }); - - cl.x("p5"); - }).catch(failTestOnCaughtError); - }); - - it("set() updates Firebase when changing a pre-existing key to the same location", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "p5"], expect, done); - - geoFire.set("loc1", [0, 0]).then(function() { - cl.x("p1"); - - return geoFire.set("loc2", [50, 50]); - }).then(function() { - cl.x("p2"); - - return geoFire.set("loc3", [-90, -90]); - }).then(function() { - cl.x("p3"); - - return geoFire.set("loc1", [0, 0]); - }).then(function() { - cl.x("p4"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc1": { ".priority": "7zzzzzzzzz", "l": { "0": 0, "1": 0 }, "g": "7zzzzzzzzz" }, - "loc2": { ".priority": "v0gs3y0zh7", "l": { "0": 50, "1": 50 }, "g": "v0gs3y0zh7" }, - "loc3": { ".priority": "1bpbpbpbpb", "l": { "0": -90, "1": -90 }, "g": "1bpbpbpbpb" } - }); - - cl.x("p5"); - }).catch(failTestOnCaughtError); - }); - - it("set() handles multiple keys at the same location", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4"], expect, done); - - geoFire.set("loc1", [0, 0]).then(function() { - cl.x("p1"); - - return geoFire.set("loc2", [0, 0]); - }).then(function() { - cl.x("p2"); - - return geoFire.set("loc3", [0, 0]); - }).then(function() { - cl.x("p3"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc1": { ".priority": "7zzzzzzzzz", "l": { "0": 0, "1": 0 }, "g": "7zzzzzzzzz" }, - "loc2": { ".priority": "7zzzzzzzzz", "l": { "0": 0, "1": 0 }, "g": "7zzzzzzzzz" }, - "loc3": { ".priority": "7zzzzzzzzz", "l": { "0": 0, "1": 0 }, "g": "7zzzzzzzzz" } - }); - - cl.x("p4"); - }).catch(failTestOnCaughtError); - }); - - it("set() updates Firebase after complex operations", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9", "p10", "p11"], expect, done); - - geoFire.set("loc:1", [0, 0]).then(function() { - cl.x("p1"); - - return geoFire.set("loc2", [50, 50]); - }).then(function() { - cl.x("p2"); - - return geoFire.set("loc%!A72f()3", [-90, -90]); - }).then(function() { - cl.x("p3"); - - return geoFire.remove("loc2"); - }).then(function() { - cl.x("p4"); - - return geoFire.set("loc2", [0.2358, -72.621]); - }).then(function() { - cl.x("p5"); - - return geoFire.set("loc4", [87.6, -130]); - }).then(function() { - cl.x("p6"); - - return geoFire.set("loc5", [5, 55.555]); - }).then(function() { - cl.x("p7"); - - return geoFire.set("loc5", null); - }).then(function() { - cl.x("p8"); - - return geoFire.set("loc:1", [87.6, -130]); - }).then(function() { - cl.x("p9"); - - return geoFire.set("loc6", [-72.258, 0.953215]); - }).then(function() { - cl.x("p10"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc:1": { ".priority": "cped3g0fur", "l": { "0": 87.6, "1": -130 }, "g": "cped3g0fur" }, - "loc2": { ".priority": "d2h376zj8h", "l": { "0": 0.2358, "1": -72.621 }, "g": "d2h376zj8h" }, - "loc%!A72f()3": { ".priority": "1bpbpbpbpb", "l": { "0": -90, "1": -90 }, "g": "1bpbpbpbpb" }, - "loc4": { ".priority": "cped3g0fur", "l": { "0": 87.6, "1": -130 }, "g": "cped3g0fur" }, - "loc6": { ".priority": "h50svty4es", "l": { "0": -72.258, "1": 0.953215 }, "g": "h50svty4es" } - }); - - cl.x("p11"); - }).catch(failTestOnCaughtError); - }); - - it("set() does not throw errors given valid keys", function() { - validKeys.forEach(function(validKey) { - expect(function() { - geoFire.set(validKey, [0, 0]); - }).not.toThrow(); - }); - }); - - it("set() throws errors given invalid keys", function() { - invalidKeys.forEach(function(invalidKey) { - expect(function() { - geoFire.set(invalidKey, [0, 0]); - }).toThrow(); - }); - }); - - it("set() does not throw errors given valid locations", function() { - validLocations.forEach(function(validLocation, i) { - expect(function() { - geoFire.set("loc", validLocation); - }).not.toThrow(); - }); - }); - - it("set() throws errors given invalid locations", function() { - invalidLocations.forEach(function(invalidLocation, i) { - // Setting location to null is valid since it will remove the key - if (invalidLocation !== null) { - expect(function() { - geoFire.set("loc", invalidLocation); - }).toThrow(); - } - }); - }); - }); - - describe("Adding multiple locations via set():", function() { - it("set() returns a promise", function(done) { - - var cl = new Checklist(["p1"], expect, done); - - geoFire.set({ - "loc1": [0, 0] - }).then(function() { - cl.x("p1"); - }); - }); - - it("set() updates Firebase when adding new locations", function(done) { - var cl = new Checklist(["p1", "p2"], expect, done); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, 50], - "loc3": [-90, -90] - }).then(function() { - cl.x("p1"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc1": { ".priority": "7zzzzzzzzz", "l": { "0": 0, "1": 0 }, "g": "7zzzzzzzzz" }, - "loc2": { ".priority": "v0gs3y0zh7", "l": { "0": 50, "1": 50 }, "g": "v0gs3y0zh7" }, - "loc3": { ".priority": "1bpbpbpbpb", "l": { "0": -90, "1": -90 }, "g": "1bpbpbpbpb" } - }); - - cl.x("p2"); - }).catch(failTestOnCaughtError); - }); - - it("set() handles decimal latitudes and longitudes", function(done) { - var cl = new Checklist(["p1", "p2"], expect, done); - - geoFire.set({ - "loc1": [0.254, 0], - "loc2": [50, 50.293403], - "loc3": [-82.614, -90.938] - }).then(function() { - cl.x("p1"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc1": { ".priority": "ebpcrypzxv", "l": { "0": 0.254, "1": 0 }, "g": "ebpcrypzxv" }, - "loc2": { ".priority": "v0gu2qnx15", "l": { "0": 50, "1": 50.293403 }, "g": "v0gu2qnx15" }, - "loc3": { ".priority": "1cr648sfx4", "l": { "0": -82.614, "1": -90.938 }, "g": "1cr648sfx4" } - }); - - cl.x("p2"); - }).catch(failTestOnCaughtError); - }); - - it("set() updates Firebase when changing a pre-existing key", function(done) { - var cl = new Checklist(["p1", "p2", "p3"], expect, done); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, 50], - "loc3": [-90, -90] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [2, 3] - }); - }).then(function() { - cl.x("p2"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc1": { ".priority": "s065kk0dc5", "l": { "0": 2, "1": 3 }, "g": "s065kk0dc5" }, - "loc2": { ".priority": "v0gs3y0zh7", "l": { "0": 50, "1": 50 }, "g": "v0gs3y0zh7" }, - "loc3": { ".priority": "1bpbpbpbpb", "l": { "0": -90, "1": -90 }, "g": "1bpbpbpbpb" } - }); - - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("set() updates Firebase when changing a pre-existing key to the same location", function(done) { - var cl = new Checklist(["p1", "p2", "p3"], expect, done); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, 50], - "loc3": [-90, -90] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [0, 0] - }); - }).then(function() { - cl.x("p2"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc1": { ".priority": "7zzzzzzzzz", "l": { "0": 0, "1": 0 }, "g": "7zzzzzzzzz" }, - "loc2": { ".priority": "v0gs3y0zh7", "l": { "0": 50, "1": 50 }, "g": "v0gs3y0zh7" }, - "loc3": { ".priority": "1bpbpbpbpb", "l": { "0": -90, "1": -90 }, "g": "1bpbpbpbpb" } - }); - - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("set() handles multiple keys at the same location", function(done) { - var cl = new Checklist(["p1", "p2"], expect, done); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [0, 0], - "loc3": [0, 0] - }).then(function() { - cl.x("p1"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc1": { ".priority": "7zzzzzzzzz", "l": { "0": 0, "1": 0 }, "g": "7zzzzzzzzz" }, - "loc2": { ".priority": "7zzzzzzzzz", "l": { "0": 0, "1": 0 }, "g": "7zzzzzzzzz" }, - "loc3": { ".priority": "7zzzzzzzzz", "l": { "0": 0, "1": 0 }, "g": "7zzzzzzzzz" } - }); - - cl.x("p2"); - }).catch(failTestOnCaughtError); - }); - - it("set() updates Firebase after complex operations", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "p5", "p6"], expect, done); - - geoFire.set({ - "loc:1": [0, 0], - "loc2": [50, 50], - "loc%!A72f()3": [-90, -90] - }).then(function() { - cl.x("p1"); - - return geoFire.remove("loc2"); - }).then(function() { - cl.x("p2"); - - return geoFire.set({ - "loc2": [0.2358, -72.621], - "loc4": [87.6, -130], - "loc5": [5, 55.555] - }); - }).then(function() { - cl.x("p3"); - - return geoFire.set({ - "loc5": null - }); - }).then(function() { - cl.x("p4"); - - return geoFire.set({ - "loc:1": [87.6, -130], - "loc6": [-72.258, 0.953215] - }); - }).then(function() { - cl.x("p5"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc:1": { ".priority": "cped3g0fur", "l": { "0": 87.6, "1": -130 }, "g": "cped3g0fur" }, - "loc2": { ".priority": "d2h376zj8h", "l": { "0": 0.2358, "1": -72.621 }, "g": "d2h376zj8h" }, - "loc%!A72f()3": { ".priority": "1bpbpbpbpb", "l": { "0": -90, "1": -90 }, "g": "1bpbpbpbpb" }, - "loc4": { ".priority": "cped3g0fur", "l": { "0": 87.6, "1": -130 }, "g": "cped3g0fur" }, - "loc6": { ".priority": "h50svty4es", "l": { "0": -72.258, "1": 0.953215 }, "g": "h50svty4es" } - }); - - cl.x("p6"); - }).catch(failTestOnCaughtError); - }); - - it("set() does not throw errors given valid keys", function() { - validKeys.forEach(function(validKey) { - expect(function() { - var locations = {}; - locations[validKey] = [0, 0]; - geoFire.set(locations); - }).not.toThrow(); - }); - }); - - it("set() throws errors given invalid keys", function() { - invalidKeys.forEach(function(invalidKey) { - if (invalidKey !== null && invalidKey !== undefined && typeof invalidKey !== "boolean") { - expect(function() { - var locations = {}; - locations[invalidKey] = [0, 0]; - geoFire.set(locations); - }).toThrow(); - } - }); - }); - - it("set() throws errors given a location argument in combination with an object", function() { - expect(function() { - geoFire.set({ - "loc": [0, 0] - }, [0, 0]); - }).toThrow(); - }); - - it("set() does not throw errors given valid locations", function() { - validLocations.forEach(function(validLocation, i) { - expect(function() { - geoFire.set({ - "loc": validLocation - }); - }).not.toThrow(); - }); - }); - - it("set() throws errors given invalid locations", function() { - invalidLocations.forEach(function(invalidLocation, i) { - // Setting location to null is valid since it will remove the key - if (invalidLocation !== null) { - expect(function() { - geoFire.set({ - "loc": invalidLocation - }); - }).toThrow(); - } - }); - }); - }); - - describe("Retrieving locations:", function() { - it("get() returns a promise", function(done) { - var cl = new Checklist(["p1"], expect, done); - - geoFire.get("loc1").then(function() { - cl.x("p1"); - }); - }); - - it("get() returns null for non-existent keys", function(done) { - var cl = new Checklist(["p1"], expect, done); - - geoFire.get("loc1").then(function(location) { - expect(location).toBeNull(); - - cl.x("p1"); - }); - }); - - it("get() retrieves locations given existing keys", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4"], expect, done); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, 50], - "loc3": [-90, -90] - }).then(function() { - cl.x("p1"); - - return geoFire.get("loc1"); - }).then(function(location) { - expect(location).toEqual([0, 0]); - cl.x("p2"); - - return geoFire.get("loc2"); - }).then(function(location) { - expect(location).toEqual([50, 50]); - cl.x("p3"); - - return geoFire.get("loc3"); - }).then(function(location) { - expect(location).toEqual([-90, -90]); - cl.x("p4"); - }).catch(failTestOnCaughtError); - }); - - it("get() does not throw errors given valid keys", function() { - validKeys.forEach(function(validKey) { - expect(function() { geoFire.get(validKey); }).not.toThrow(); - }); - }); - - it("get() throws errors given invalid keys", function() { - invalidKeys.forEach(function(invalidKey) { - expect(function() { geoFire.get(invalidKey); }).toThrow(); - }); - }); - }); - - describe("Removing locations:", function() { - it("set() removes existing location given null", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "p5"], expect, done); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [2, 3] - }).then(function() { - cl.x("p1"); - - return geoFire.get("loc1"); - }).then(function(location) { - expect(location).toEqual([0, 0]); - - cl.x("p2"); - - return geoFire.set("loc1", null); - }).then(function() { - cl.x("p3"); - - return geoFire.get("loc1"); - }).then(function(location) { - expect(location).toBeNull(); - - cl.x("p4"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc2": { ".priority": "s065kk0dc5", "l": { "0": 2, "1": 3 }, "g": "s065kk0dc5" } - }); - - cl.x("p5"); - }).catch(failTestOnCaughtError); - }); - - it("set() does nothing given a non-existent location and null", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "p5"], expect, done); - - geoFire.set("loc1", [0, 0]).then(function() { - cl.x("p1"); - - return geoFire.get("loc1"); - }).then(function(location) { - expect(location).toEqual([0, 0]); - - cl.x("p2"); - - return geoFire.set("loc2", null); - }).then(function() { - cl.x("p3"); - - return geoFire.get("loc2"); - }).then(function(location) { - expect(location).toBeNull(); - - cl.x("p4"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc1": { ".priority": "7zzzzzzzzz", "l": { "0": 0, "1": 0 }, "g": "7zzzzzzzzz" } - }); - - cl.x("p5"); - }).catch(failTestOnCaughtError); - }); - - it("set() removes existing location given null", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "p5"], expect, done); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [2, 3] - }).then(function() { - cl.x("p1"); - - return geoFire.get("loc1"); - }).then(function(location) { - expect(location).toEqual([0, 0]); - - cl.x("p2"); - - return geoFire.set({ - "loc1": null, - "loc3": [-90, -90] - }); - }).then(function() { - cl.x("p3"); - - return geoFire.get("loc1"); - }).then(function(location) { - expect(location).toBeNull(); - - cl.x("p4"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc2": { ".priority": "s065kk0dc5", "l": { "0": 2, "1": 3 }, "g": "s065kk0dc5" }, - "loc3": { ".priority": "1bpbpbpbpb", "l": { "0": -90, "1": -90 }, "g": "1bpbpbpbpb" } - }); - - cl.x("p5"); - }).catch(failTestOnCaughtError); - }); - - it("set() does nothing given a non-existent location and null", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4"], expect, done); - - geoFire.set({ - "loc1": [0, 0], - "loc2": null - }).then(function() { - cl.x("p1"); - - return geoFire.get("loc1"); - }).then(function(location) { - expect(location).toEqual([0, 0]); - - cl.x("p2"); - - return geoFire.get("loc2"); - }).then(function(location) { - expect(location).toBeNull(); - - cl.x("p3"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc1": { ".priority": "7zzzzzzzzz", "l": { "0": 0, "1": 0 }, "g": "7zzzzzzzzz" } - }); - - cl.x("p4"); - }).catch(failTestOnCaughtError); - }); - - it("remove() removes existing location", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "p5"], expect, done); - - geoFire.set({ - "loc:^%*1": [0, 0], - "loc2": [2, 3] - }).then(function() { - cl.x("p1"); - - return geoFire.get("loc:^%*1"); - }).then(function(location) { - expect(location).toEqual([0, 0]); - - cl.x("p2"); - - return geoFire.remove("loc:^%*1"); - }).then(function() { - cl.x("p3"); - - return geoFire.get("loc:^%*1"); - }).then(function(location) { - expect(location).toBeNull(); - - cl.x("p4"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc2": { ".priority": "s065kk0dc5", "l": { "0": 2, "1": 3 }, "g": "s065kk0dc5" } - }); - - cl.x("p5"); - }).catch(failTestOnCaughtError); - }); - - it("remove() does nothing given a non-existent location", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "p5"], expect, done); - - geoFire.set("loc1", [0, 0]).then(function() { - cl.x("p1"); - - return geoFire.get("loc1"); - }).then(function(location) { - expect(location).toEqual([0, 0]); - - cl.x("p2"); - - return geoFire.remove("loc2"); - }).then(function() { - cl.x("p3"); - - return geoFire.get("loc2"); - }).then(function(location) { - expect(location).toBeNull(); - - cl.x("p4"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc1": { ".priority": "7zzzzzzzzz", "l": { "0": 0, "1": 0 }, "g": "7zzzzzzzzz" } - }); - - cl.x("p5"); - }).catch(failTestOnCaughtError); - }); - - it("remove() only removes one key if multiple keys are at the same location", function(done) { - var cl = new Checklist(["p1", "p2", "p3"], expect, done); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [2, 3], - "loc3": [0, 0] - }).then(function() { - cl.x("p1"); - - return geoFire.remove("loc1"); - }).then(function() { - cl.x("p2"); - - return getFirebaseData(); - }).then(function(firebaseData) { - expect(firebaseData).toEqual({ - "loc2": { ".priority": "s065kk0dc5", "l": { "0": 2, "1": 3 }, "g": "s065kk0dc5" }, - "loc3": { ".priority": "7zzzzzzzzz", "l": { "0": 0, "1": 0 }, "g": "7zzzzzzzzz" } - }); - - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("remove() does not throw errors given valid keys", function() { - validKeys.forEach(function(validKey) { - expect(function() { geoFire.remove(validKey); }).not.toThrow(); - }); - }); - - it("remove() throws errors given invalid keys", function() { - invalidKeys.forEach(function(invalidKey) { - expect(function() { geoFire.remove(invalidKey); }).toThrow(); - }); - }); - }); - - describe("query():", function() { - it("query() returns GeoQuery instance", function() { - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - expect(geoQueries[0] instanceof GeoQuery).toBeTruthy(); - }); - - it("query() does not throw errors given valid query criteria", function() { - validQueryCriterias.forEach(function(validQueryCriteria) { - if (typeof validQueryCriteria.center !== "undefined" && typeof validQueryCriteria.radius !== "undefined") { - expect(function() { geoFire.query(validQueryCriteria); }).not.toThrow(); - } - }); - }); - - it("query() throws errors given invalid query criteria", function() { - invalidQueryCriterias.forEach(function(invalidQueryCriteria) { - expect(function() { geoFire.query(invalidQueryCriteria); }).toThrow(); - }); - }); - }); -}); diff --git a/tests/specs/geoFireUtils.spec.js b/tests/specs/geoFireUtils.spec.js deleted file mode 100644 index ce10ba54..00000000 --- a/tests/specs/geoFireUtils.spec.js +++ /dev/null @@ -1,269 +0,0 @@ -describe("geoFireUtils Tests:", function() { - describe("Parameter validation:", function() { - it("validateKey() does not throw errors given valid keys", function() { - validKeys.forEach(function(validKey) { - expect(function() { validateKey(validKey); }).not.toThrow(); - }); - }); - - it("validateKey() throws errors given invalid keys", function() { - invalidKeys.forEach(function(invalidKey) { - expect(function() { validateKey(invalidKey); }).toThrow(); - }); - }); - - it("validateLocation() does not throw errors given valid locations", function() { - validLocations.forEach(function(validLocation, i) { - expect(function() { validateLocation(validLocation); }).not.toThrow(); - }); - }); - - it("validateLocation() throws errors given invalid locations", function() { - invalidLocations.forEach(function(invalidLocation, i) { - expect(function() { validateLocation(invalidLocation); }).toThrow(); - }); - }); - - it("validateGeohash() does not throw errors given valid geohashes", function() { - validGeohashes.forEach(function(validGeohash, i) { - expect(function() { validateGeohash(validGeohash); }).not.toThrow(); - }); - }); - - it("validateGeohash() throws errors given invalid geohashes", function() { - invalidGeohashes.forEach(function(invalidGeohash, i) { - expect(function() { validateGeohash(invalidGeohash); }).toThrow(); - }); - }); - - it("validateCriteria(criteria, true) does not throw errors given valid query criteria", function() { - validQueryCriterias.forEach(function(validQueryCriteria) { - if (typeof validQueryCriteria.center !== "undefined" && typeof validQueryCriteria.radius !== "undefined") { - expect(function() { validateCriteria(validQueryCriteria, true); }).not.toThrow(); - } - }); - }); - - it("validateCriteria(criteria) does not throw errors given valid query criteria", function() { - validQueryCriterias.forEach(function(validQueryCriteria) { - expect(function() { validateCriteria(validQueryCriteria); }).not.toThrow(); - }); - }); - - it("validateCriteria(criteria, true) throws errors given invalid query criteria", function() { - invalidQueryCriterias.forEach(function(invalidQueryCriteria) { - expect(function() { validateCriteria(invalidQueryCriteria, true); }).toThrow(); - }); - expect(function() { validateCriteria({center: [0, 0]}, true); }).toThrow(); - expect(function() { validateCriteria({radius: 1000}, true); }).toThrow(); - }); - - it("validateCriteria(criteria) throws errors given invalid query criteria", function() { - invalidQueryCriterias.forEach(function(invalidQueryCriteria) { - expect(function() { validateCriteria(invalidQueryCriteria); }).toThrow(); - }); - }); - }); - - describe("Distance calculations:", function() { - it("degreesToRadians() converts degrees to radians", function() { - expect(degreesToRadians(0)).toBeCloseTo(0); - expect(degreesToRadians(45)).toBeCloseTo(0.7854, 4); - expect(degreesToRadians(90)).toBeCloseTo(1.5708, 4); - expect(degreesToRadians(135)).toBeCloseTo(2.3562, 4); - expect(degreesToRadians(180)).toBeCloseTo(3.1416, 4); - expect(degreesToRadians(225)).toBeCloseTo(3.9270, 4); - expect(degreesToRadians(270)).toBeCloseTo(4.7124, 4); - expect(degreesToRadians(315)).toBeCloseTo(5.4978, 4); - expect(degreesToRadians(360)).toBeCloseTo(6.2832, 4); - expect(degreesToRadians(-45)).toBeCloseTo(-0.7854, 4); - expect(degreesToRadians(-90)).toBeCloseTo(-1.5708, 4); - }); - - it("degreesToRadians() throws errors given invalid inputs", function() { - expect(function() { degreesToRadians(""); }).toThrow(); - expect(function() { degreesToRadians("a"); }).toThrow(); - expect(function() { degreesToRadians(true); }).toThrow(); - expect(function() { degreesToRadians(false); }).toThrow(); - expect(function() { degreesToRadians([1]); }).toThrow(); - expect(function() { degreesToRadians({}); }).toThrow(); - expect(function() { degreesToRadians(null); }).toThrow(); - expect(function() { degreesToRadians(undefined); }).toThrow(); - }); - - it("dist() calculates the distance between locations", function() { - expect(GeoFire.distance([90, 180], [90, 180])).toBeCloseTo(0, 0); - expect(GeoFire.distance([-90, -180], [90, 180])).toBeCloseTo(20015, 0); - expect(GeoFire.distance([-90, -180], [-90, 180])).toBeCloseTo(0, 0); - expect(GeoFire.distance([-90, -180], [90, -180])).toBeCloseTo(20015, 0); - expect(GeoFire.distance([37.7853074, -122.4054274], [78.216667, 15.55])).toBeCloseTo(6818, 0); - expect(GeoFire.distance([38.98719, -77.250783], [29.3760648, 47.9818853])).toBeCloseTo(10531, 0); - expect(GeoFire.distance([38.98719, -77.250783], [-54.933333, -67.616667])).toBeCloseTo(10484, 0); - expect(GeoFire.distance([29.3760648, 47.9818853], [-54.933333, -67.616667])).toBeCloseTo(14250, 0); - expect(GeoFire.distance([-54.933333, -67.616667], [-54, -67])).toBeCloseTo(111, 0); - }); - - it("dist() does not throw errors given valid locations", function() { - validLocations.forEach(function(validLocation, i) { - expect(function() { GeoFire.distance(validLocation, [0, 0]); }).not.toThrow(); - expect(function() { GeoFire.distance([0, 0], validLocation); }).not.toThrow(); - }); - }); - - it("dist() throws errors given invalid locations", function() { - invalidLocations.forEach(function(invalidLocation, i) { - expect(function() { GeoFire.distance(invalidLocation, [0, 0]); }).toThrow(); - expect(function() { GeoFire.distance([0, 0], invalidLocation); }).toThrow(); - }); - }); - }); - - describe("Geohashing:", function() { - it("encodeGeohash() encodes locations to geohashes given no precision", function() { - expect(encodeGeohash([-90, -180])).toBe("000000000000".slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([90, 180])).toBe("zzzzzzzzzzzz".slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([-90, 180])).toBe("pbpbpbpbpbpb".slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([90, -180])).toBe("bpbpbpbpbpbp".slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([37.7853074, -122.4054274])).toBe("9q8yywe56gcf".slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([38.98719, -77.250783])).toBe("dqcjf17sy6cp".slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([29.3760648, 47.9818853])).toBe("tj4p5gerfzqu".slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([78.216667, 15.55])).toBe("umghcygjj782".slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([-54.933333, -67.616667])).toBe("4qpzmren1kwb".slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([-54, -67])).toBe("4w2kg3s54y7h".slice(0, g_GEOHASH_PRECISION)); - }); - - it("encodeGeohash() encodes locations to geohashes given a custom precision", function() { - expect(encodeGeohash([-90, -180], 6)).toBe("000000"); - expect(encodeGeohash([90, 180], 20)).toBe("zzzzzzzzzzzzzzzzzzzz"); - expect(encodeGeohash([-90, 180], 1)).toBe("p"); - expect(encodeGeohash([90, -180], 5)).toBe("bpbpb"); - expect(encodeGeohash([37.7853074, -122.4054274], 8)).toBe("9q8yywe5"); - expect(encodeGeohash([38.98719, -77.250783], 18)).toBe("dqcjf17sy6cppp8vfn"); - expect(encodeGeohash([29.3760648, 47.9818853], 12)).toBe("tj4p5gerfzqu"); - expect(encodeGeohash([78.216667, 15.55], 1)).toBe("u"); - expect(encodeGeohash([-54.933333, -67.616667], 7)).toBe("4qpzmre"); - expect(encodeGeohash([-54, -67], 9)).toBe("4w2kg3s54"); - }); - - it("encodeGeohash() does not throw errors given valid locations", function() { - validLocations.forEach(function(validLocation, i) { - expect(function() { encodeGeohash(validLocation); }).not.toThrow(); - }); - }); - - it("encodeGeohash() throws errors given invalid locations", function() { - invalidLocations.forEach(function(invalidLocation, i) { - expect(function() { encodeGeohash(invalidLocation); }).toThrow(); - }); - }); - - it("encodeGeohash() does not throw errors given valid precision", function() { - var validPrecisions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, undefined]; - - validPrecisions.forEach(function(validPrecision, i) { - expect(function() { encodeGeohash([0, 0], validPrecision); }).not.toThrow(); - }); - }); - - it("encodeGeohash() throws errors given invalid precision", function() { - var invalidPrecisions = [0, -1, 1.5, 23, "", "a", true, false, [], {}, [1], {a:1}, null]; - - invalidPrecisions.forEach(function(invalidPrecision, i) { - expect(function() { encodeGeohash([0, 0], invalidPrecision); }).toThrow(); - }); - }); - }); - - describe("Coordinate calculations:", function() { - it("metersToLongtitudeDegrees calculates correctly", function() { - expect(metersToLongitudeDegrees(1000, 0)).toBeCloseTo(0.008983, 5); - expect(metersToLongitudeDegrees(111320, 0)).toBeCloseTo(1, 5); - expect(metersToLongitudeDegrees(107550, 15)).toBeCloseTo(1, 5); - expect(metersToLongitudeDegrees(96486, 30)).toBeCloseTo(1, 5); - expect(metersToLongitudeDegrees(78847, 45)).toBeCloseTo(1, 5); - expect(metersToLongitudeDegrees(55800, 60)).toBeCloseTo(1, 5); - expect(metersToLongitudeDegrees(28902, 75)).toBeCloseTo(1, 5); - expect(metersToLongitudeDegrees(0, 90)).toBeCloseTo(0, 5); - expect(metersToLongitudeDegrees(1000, 90)).toBeCloseTo(360, 5); - expect(metersToLongitudeDegrees(1000, 89.9999)).toBeCloseTo(360, 5); - expect(metersToLongitudeDegrees(1000, 89.995)).toBeCloseTo(102.594208, 5); - }); - - it("wrapLongitude wraps correctly", function() { - expect(wrapLongitude(0)).toBeCloseTo(0, 6); - expect(wrapLongitude(180)).toBeCloseTo(180, 6); - expect(wrapLongitude(-180)).toBeCloseTo(-180, 6); - expect(wrapLongitude(182)).toBeCloseTo(-178, 6); - expect(wrapLongitude(270)).toBeCloseTo(-90, 6); - expect(wrapLongitude(360)).toBeCloseTo(0, 6); - expect(wrapLongitude(540)).toBeCloseTo(-180, 6); - expect(wrapLongitude(630)).toBeCloseTo(-90, 6); - expect(wrapLongitude(720)).toBeCloseTo(0, 6); - expect(wrapLongitude(810)).toBeCloseTo(90, 6); - expect(wrapLongitude(-360)).toBeCloseTo(0, 6); - expect(wrapLongitude(-182)).toBeCloseTo(178, 6); - expect(wrapLongitude(-270)).toBeCloseTo(90, 6); - expect(wrapLongitude(-360)).toBeCloseTo(0, 6); - expect(wrapLongitude(-450)).toBeCloseTo(-90, 6); - expect(wrapLongitude(-540)).toBeCloseTo(180, 6); - expect(wrapLongitude(-630)).toBeCloseTo(90, 6); - expect(wrapLongitude(1080)).toBeCloseTo(0, 6); - expect(wrapLongitude(-1080)).toBeCloseTo(0, 6); - }); - }); - - describe("Bounding box bits:", function() { - it("boundingBoxBits must return correct number of bits", function() { - expect(boundingBoxBits([35,0], 1000)).toBe(28); - expect(boundingBoxBits([35.645,0], 1000)).toBe(27); - expect(boundingBoxBits([36,0], 1000)).toBe(27); - expect(boundingBoxBits([0,0], 1000)).toBe(28); - expect(boundingBoxBits([0,-180], 1000)).toBe(28); - expect(boundingBoxBits([0,180], 1000)).toBe(28); - expect(boundingBoxBits([0,0], 8000)).toBe(22); - expect(boundingBoxBits([45,0], 1000)).toBe(27); - expect(boundingBoxBits([75,0], 1000)).toBe(25); - expect(boundingBoxBits([75,0], 2000)).toBe(23); - expect(boundingBoxBits([90,0], 1000)).toBe(1); - expect(boundingBoxBits([90,0], 2000)).toBe(1); - }); - }); - - describe("Geohash queries:", function() { - it("Geohash queries must be of the right size", function() { - expect(geohashQuery("64m9yn96mx",6)).toEqual(["60", "6h"]); - expect(geohashQuery("64m9yn96mx",1)).toEqual(["0", "h"]); - expect(geohashQuery("64m9yn96mx",10)).toEqual(["64", "65"]); - expect(geohashQuery("6409yn96mx",11)).toEqual(["640", "64h"]); - expect(geohashQuery("64m9yn96mx",11)).toEqual(["64h", "64~"]); - expect(geohashQuery("6",10)).toEqual(["6", "6~"]); - expect(geohashQuery("64z178",12)).toEqual(["64s", "64~"]); - expect(geohashQuery("64z178",15)).toEqual(["64z", "64~"]); - }); - - it("Queries from geohashQueries must contain points in circle", function() { - function inQuery(queries, hash) { - for (var i = 0; i < queries.length; i++) { - if (hash >= queries[i][0] && hash < queries[i][1]) { - return true; - } - } - return false; - } - for (var i = 0; i < 200; i++) { - var centerLat = Math.pow(Math.random(),5)*160-80; - var centerLong = Math.pow(Math.random(),5)*360-180; - var radius = Math.random()*Math.random()*100000; - var degreeRadius = metersToLongitudeDegrees(radius, centerLat); - var queries = geohashQueries([centerLat, centerLong], radius); - for (var j = 0; j < 1000; j++) { - var pointLat = Math.max(-89.9, Math.min(89.9, centerLat + Math.random()*degreeRadius)); - var pointLong = wrapLongitude(centerLong + Math.random()*degreeRadius); - if (GeoFire.distance([centerLat, centerLong], [pointLat, pointLong]) < radius/1000) { - expect(inQuery(queries, encodeGeohash([pointLat, pointLong]))).toBe(true); - } - } - } - }); - }); -}); diff --git a/tests/specs/geoQuery.spec.js b/tests/specs/geoQuery.spec.js deleted file mode 100644 index e846ac94..00000000 --- a/tests/specs/geoQuery.spec.js +++ /dev/null @@ -1,1404 +0,0 @@ -describe("GeoQuery Tests:", function() { - // Reset the Firebase before each test - beforeEach(function(done) { - beforeEachHelper(done); - }); - - afterEach(function(done) { - afterEachHelper(done); - }); - - describe("Constructor:", function() { - it("Constructor stores query criteria", function() { - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - expect(geoQueries[0].center()).toEqual([1,2]); - expect(geoQueries[0].radius()).toEqual(1000); - }); - - it("Constructor throws error on invalid query criteria", function() { - expect(function() { geoFire.query({}) }).toThrow(); - expect(function() { geoFire.query({random: 100}) }).toThrow(); - expect(function() { geoFire.query({center: [1,2]}) }).toThrow(); - expect(function() { geoFire.query({radius: 1000}) }).toThrow(); - expect(function() { geoFire.query({center: [91,2], radius: 1000}) }).toThrow(); - expect(function() { geoFire.query({center: [1,-181], radius: 1000}) }).toThrow(); - expect(function() { geoFire.query({center: ["text",2], radius: 1000}) }).toThrow(); - expect(function() { geoFire.query({center: [1,[1,2]], radius: 1000}) }).toThrow(); - expect(function() { geoFire.query({center: 1000, radius: 1000}) }).toThrow(); - expect(function() { geoFire.query({center: null, radius: 1000}) }).toThrow(); - expect(function() { geoFire.query({center: undefined, radius: 1000}) }).toThrow(); - expect(function() { geoFire.query({center: [null,2], radius: 1000}) }).toThrow(); - expect(function() { geoFire.query({center: [1,undefined], radius: 1000}) }).toThrow(); - expect(function() { geoFire.query({center: [1,2], radius: -10}) }).toThrow(); - expect(function() { geoFire.query({center: [1,2], radius: "text"}) }).toThrow(); - expect(function() { geoFire.query({center: [1,2], radius: [1,2]}) }).toThrow(); - expect(function() { geoFire.query({center: [1,2], radius: null}) }).toThrow(); - expect(function() { geoFire.query({center: [1,2], radius: undefined}) }).toThrow(); - expect(function() { geoFire.query({center: [1,2], radius: 1000, other: "throw"}) }).toThrow(); - }); - }); - - describe("updateCriteria():", function() { - it("updateCriteria() updates query criteria", function() { - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - expect(geoQueries[0].center()).toEqual([1,2]); - expect(geoQueries[0].radius()).toEqual(1000); - - geoQueries[0].updateCriteria({center: [2,3], radius: 100}); - - expect(geoQueries[0].center()).toEqual([2,3]); - expect(geoQueries[0].radius()).toEqual(100); - }); - - it("updateCriteria() updates query criteria when given only center", function() { - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - expect(geoQueries[0].center()).toEqual([1,2]); - expect(geoQueries[0].radius()).toEqual(1000); - - geoQueries[0].updateCriteria({center: [2,3]}); - - expect(geoQueries[0].center()).toEqual([2,3]); - expect(geoQueries[0].radius()).toEqual(1000); - }); - - it("updateCriteria() updates query criteria when given only radius", function() { - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - expect(geoQueries[0].center()).toEqual([1,2]); - expect(geoQueries[0].radius()).toEqual(1000); - - geoQueries[0].updateCriteria({radius: 100}); - - expect(geoQueries[0].center()).toEqual([1,2]); - expect(geoQueries[0].radius()).toEqual(100); - }); - - it("updateCriteria() fires \"key_entered\" callback for locations which now belong to the GeoQuery", function(done) { - var cl = new Checklist(["p1", "p2", "loc1 entered", "loc4 entered"], expect, done); - - geoQueries.push(geoFire.query({center: [90,90], radius: 1000})); - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - - geoFire.set({ - "loc1": [2, 3], - "loc2": [50, -7], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [67, 55] - }).then(function() { - cl.x("p1"); - - geoQueries[0].updateCriteria({center: [1,2], radius: 1000}); - - return wait(100); - }).then(function() { - cl.x("p2"); - }).catch(failTestOnCaughtError); - }); - - it("updateCriteria() fires \"key_entered\" callback for locations with complex keys which now belong to the GeoQuery", function(done) { - var cl = new Checklist(["p1", "p2", "loc:^:*1 entered", "loc-+-+-4 entered"], expect, done); - - geoQueries.push(geoFire.query({center: [90,90], radius: 1000})); - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - - geoFire.set({ - "loc:^:*1": [2, 3], - "loc:a:a:a:a:2": [50, -7], - "loc%!@3": [16, -150], - "loc-+-+-4": [5, 5], - "loc:5": [67, 55] - }).then(function() { - cl.x("p1"); - - geoQueries[0].updateCriteria({center: [1,2], radius: 1000}); - - return wait(100); - }).then(function() { - cl.x("p2"); - }).catch(failTestOnCaughtError); - }); - - it("updateCriteria() fires \"key_exited\" callback for locations which no longer belong to the GeoQuery", function(done) { - var cl = new Checklist(["p1", "p2", "loc1 exited", "loc4 exited"], expect, done); - - geoQueries.push(geoFire.query({center: [1, 2], radius: 1000})); - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited"); - }); - - geoFire.set({ - "loc1": [2, 3], - "loc2": [50, -7], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [67, 55] - }).then(function() { - cl.x("p1"); - - geoQueries[0].updateCriteria({center: [90,90], radius: 1000}); - - return wait(100); - }).then(function() { - cl.x("p2"); - }).catch(failTestOnCaughtError); - }); - - it("updateCriteria() does not cause event callbacks to fire on the previous criteria", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "loc1 entered", "loc4 entered", "loc1 exited", "loc4 exited", "loc4 entered", "loc5 entered"], expect, done); - - geoQueries.push(geoFire.query({center: [1, 2], radius: 1000})); - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited"); - }); - - geoFire.set({ - "loc1": [2, 3], - "loc2": [50, -7], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [88, 88] - }).then(function() { - cl.x("p1"); - - geoQueries[0].updateCriteria({center: [90, 90], radius: 1000}); - - return wait(100); - }).then(function() { - cl.x("p2"); - - return geoFire.set({ - "loc2": [1, 1], - "loc4": [89, 90] - }); - }).then(function() { - cl.x("p3"); - - return wait(100); - }).then(function() { - cl.x("p4"); - }).catch(failTestOnCaughtError); - }); - - it("updateCriteria() does not cause \"key_moved\" callbacks to fire for keys in both the previous and updated queries", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "loc1 entered", "loc4 entered", "loc4 exited", "loc2 entered"], expect, done); - - geoQueries.push(geoFire.query({center: [1, 2], radius: 1000})); - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited"); - }); - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved"); - }); - - geoFire.set({ - "loc1": [2, 3], - "loc2": [50, -7], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [88, 88] - }).then(function() { - cl.x("p1"); - - geoQueries[0].updateCriteria({center: [1, 1], radius: 1000}); - - return wait(100); - }).then(function() { - cl.x("p2"); - - return geoFire.set({ - "loc2": [1, 1], - "loc4": [89, 90] - }); - }).then(function() { - cl.x("p3"); - - return wait(100); - }).then(function() { - cl.x("p4"); - }).catch(failTestOnCaughtError); - }); - - it("updateCriteria() does not cause \"key_exited\" callbacks to fire twice for keys in the previous query but not in the updated query and which were moved after the query was updated", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "p5", "p6", "loc1 entered", "loc4 entered", "loc1 exited", "loc4 exited", "loc4 entered", "loc5 entered", "loc5 moved"], expect, done); - - geoQueries.push(geoFire.query({center: [1, 2], radius: 1000})); - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited"); - }); - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved"); - }); - - - geoFire.set({ - "loc1": [2, 3], - "loc2": [50, -7], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [88, 88] - }).then(function() { - cl.x("p1"); - - geoQueries[0].updateCriteria({center: [90, 90], radius: 1000}); - - return wait(100); - }).then(function() { - cl.x("p2"); - - return geoFire.set({ - "loc2": [1, 1], - "loc4": [89, 90] - }); - }).then(function() { - cl.x("p3"); - - return wait(100); - }).then(function() { - cl.x("p4"); - - return geoFire.set({ - "loc2": [0, 0], - "loc5": [89, 89] - }); - }).then(function() { - cl.x("p5"); - - return wait(100); - }).then(function() { - cl.x("p6"); - }).catch(failTestOnCaughtError); - }); - - it("updateCriteria() does not throw errors given valid query criteria", function() { - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - validQueryCriterias.forEach(function(validQueryCriteria) { - expect(function() { geoQueries[0].updateCriteria(validQueryCriteria); }).not.toThrow(); - }); - }); - - it("updateCriteria() throws errors given invalid query criteria", function() { - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - invalidQueryCriterias.forEach(function(invalidQueryCriteria) { - expect(function() { geoQueries[0].updateCriteria(invalidQueryCriteria); }).toThrow(); - }); - }); - }); - - describe("on():", function() { - it("on() throws error given invalid event type", function() { - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - var setInvalidEventType = function() { - geoQueries[0].on("invalid_event", function() { }); - } - - expect(setInvalidEventType).toThrow(); - }); - - it("on() throws error given invalid callback", function() { - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - var setInvalidCallback = function() { - geoQueries[0].on("key_entered", "non-function"); - } - - expect(setInvalidCallback).toThrow(); - }); - }); - - describe("\"ready\" event:", function() { - it("\"ready\" event fires after all \"key_entered\" events have fired", function(done) { - var cl = new Checklist(["p1", "loc1 entered", "loc2 entered", "loc5 entered", "loc6 entered", "loc7 entered", "loc10 entered", "ready fired"], expect, done); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [1, 1], - "loc3": [50, 50], - "loc4": [14, 1], - "loc5": [1, 2], - "loc6": [1, 1], - "loc7": [0, 0], - "loc8": [-80, 44], - "loc9": [1, -136], - "loc10": [-2, -2] - }).then(function() { - cl.x("p1"); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - - geoQueries[0].on("ready", function() { - expect(cl.length()).toBe(1); - cl.x("ready fired"); - }); - }); - }); - - it("\"ready\" event fires immediately if the callback is added after the query is already ready", function(done) { - var cl = new Checklist(["p1", "loc1 entered", "loc2 entered", "loc5 entered", "loc6 entered", "loc7 entered", "loc10 entered", "ready1 fired", "ready2 fired"], expect, done); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [1, 1], - "loc3": [50, 50], - "loc4": [14, 1], - "loc5": [1, 2], - "loc6": [1, 1], - "loc7": [0, 0], - "loc8": [-80, 44], - "loc9": [1, -136], - "loc10": [-2, -2] - }).then(function() { - cl.x("p1"); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - - geoQueries[0].on("ready", function() { - expect(cl.length()).toBe(2); - cl.x("ready1 fired"); - geoQueries[0].on("ready", function() { - expect(cl.length()).toBe(1); - cl.x("ready2 fired"); - }); - }); - }); - }); - - it("\"ready\" event fires after increasing the query radius, even if no new geohashes were queried", function(done) { - var cl = new Checklist(["ready1 fired","ready2 fired"], expect, done); - geoQueries.push(geoFire.query({center: [37.7851382,-122.405893], radius: 6})); - var onReadyCallbackRegistration1 = geoQueries[0].on("ready", function() { - cl.x("ready1 fired"); - onReadyCallbackRegistration1.cancel(); - geoQueries[0].updateCriteria({ - radius: 7 - }); - geoQueries[0].on("ready", function() { - cl.x("ready2 fired"); - }); - }); - }); - - it("updateCriteria() fires the \"ready\" event after all \"key_entered\" events have fired", function(done) { - var cl = new Checklist(["p1", "loc1 entered", "loc2 entered", "loc5 entered", "loc3 entered", "loc1 exited", "loc2 exited", "loc5 exited", "ready1 fired", "ready2 fired"], expect, done); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [1, 1], - "loc3": [50, 50], - "loc4": [14, 1], - "loc5": [1, 2] - }).then(function() { - cl.x("p1"); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited"); - }); - - var onReadyCallbackRegistration1 = geoQueries[0].on("ready", function() { - expect(cl.length()).toBe(6); - cl.x("ready1 fired"); - - onReadyCallbackRegistration1.cancel(); - - geoQueries[0].updateCriteria({ - center: [51, 51] - }); - - geoQueries[0].on("ready", function() { - expect(cl.length()).toBe(1); - cl.x("ready2 fired"); - }); - }); - }); - }); - }); - - describe("\"key_moved\" event:", function() { - it("\"key_moved\" callback does not fire for brand new locations within or outside of the GeoQuery", function(done) { - var cl = new Checklist(["p1", "p2"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, -7], - "loc3": [1, 1] - }).then(function() { - cl.x("p1"); - - return wait(100); - }).then(function() { - cl.x("p2"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_moved\" callback does not fire for locations outside of the GeoQuery which are moved somewhere else outside of the GeoQuery", function(done) { - var cl = new Checklist(["p1", "p2", "p3"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved"); - }); - - geoFire.set({ - "loc1": [1, 90], - "loc2": [50, -7], - "loc3": [16, -150] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [1, 91], - "loc3": [-50, -50] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_moved\" callback does not fire for locations outside of the GeoQuery which are moved within the GeoQuery", function(done) { - var cl = new Checklist(["p1", "p2", "p3"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved"); - }); - - geoFire.set({ - "loc1": [1, 90], - "loc2": [50, -7], - "loc3": [16, -150] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [0, 0], - "loc3": [-1, -1] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_moved\" callback does not fire for locations within the GeoQuery which are moved somewhere outside of the GeoQuery", function(done) { - var cl = new Checklist(["p1", "p2", "p3"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, -7], - "loc3": [1, 1] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [1, 90], - "loc3": [-1, -90] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_moved\" callback does not fires for a location within the GeoQuery which is set to the same location", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc3 moved"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, -7], - "loc3": [1, -1] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [0, 0], - "loc2": [55, 55], - "loc3": [1, 1] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_moved\" callback fires for locations within the GeoQuery which are moved somewhere else within the GeoQuery", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc1 moved", "loc3 moved"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, -7], - "loc3": [1, 1] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [2, 2], - "loc3": [-1, -1] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_moved\" callback gets passed correct location parameter", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc1 moved to 2,2", "loc3 moved to -1,-1"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved to " + location); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, -7], - "loc3": [1, 1] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [2, 2], - "loc3": [-1, -1] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_moved\" callback gets passed correct distance parameter", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc1 moved (111.19 km from center)", "loc3 moved (400.90 km from center)"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved (" + distance.toFixed(2) + " km from center)"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, -7], - "loc3": [1, 1] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [2, 2], - "loc3": [-1, -1] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_moved\" callback properly fires when multiple keys are at the same location within the GeoQuery and only one of them moves somewhere else within the GeoQuery", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc1 moved", "loc3 moved"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [0, 0], - "loc3": [1, 1] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [2, 2], - "loc3": [-1, -1] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_moved\" callback properly fires when a location within the GeoQuery moves somehwere else within the GeoQuery that is already occupied by another key", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc1 moved", "loc3 moved"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [2, 2], - "loc3": [1, 1] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [2, 2], - "loc3": [-1, -1] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("multiple \"key_moved\" callbacks fire for locations within the GeoQuery which are moved somewhere else within the GeoQuery", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc1 moved1", "loc3 moved1", "loc1 moved2", "loc3 moved2"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved1"); - }); - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved2"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, -7], - "loc3": [1, 1] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [2, 2], - "loc3": [-1, -1] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - }); - - describe("\"key_entered\" event:", function() { - it("\"key_entered\" callback fires when a location enters the GeoQuery before onKeyEntered() was called", function(done) { - var cl = new Checklist(["p1", "p2", "loc1 entered", "loc4 entered"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoFire.set({ - "loc1": [2, 3], - "loc2": [50, -7], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [67, 55] - }).then(function() { - cl.x("p1"); - - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - - return wait(100); - }).then(function() { - cl.x("p2"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_entered\" callback fires when a location enters the GeoQuery after onKeyEntered() was called", function(done) { - var cl = new Checklist(["p1", "p2", "loc1 entered", "loc4 entered"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - - geoFire.set({ - "loc1": [2, 3], - "loc2": [50, -7], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [67, 55] - }).then(function() { - cl.x("p1"); - - return wait(100); - }).then(function() { - cl.x("p2"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_entered\" callback gets passed correct location parameter", function(done) { - var cl = new Checklist(["p1", "p2", "loc1 entered at 2,3", "loc4 entered at 5,5"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered at " + location); - }); - - geoFire.set({ - "loc1": [2, 3], - "loc2": [50, -7], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [67, 55] - }).then(function() { - cl.x("p1"); - - return wait(100); - }).then(function() { - cl.x("p2"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_entered\" callback gets passed correct distance parameter", function(done) { - var cl = new Checklist(["p1", "p2", "loc1 entered (157.23 km from center)", "loc4 entered (555.66 km from center)"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered (" + distance.toFixed(2) + " km from center)"); - }); - - geoFire.set({ - "loc1": [2, 3], - "loc2": [50, -7], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [67, 55] - }).then(function() { - cl.x("p1"); - - return wait(100); - }).then(function() { - cl.x("p2"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_entered\" callback properly fires when multiple keys are at the same location outside the GeoQuery and only one of them moves within the GeoQuery", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc1 entered"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - - geoFire.set({ - "loc1": [50, 50], - "loc2": [50, 50], - "loc3": [18, -121] - }).then(function() { - cl.x("p1"); - - return geoFire.set("loc1", [2, 2]); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_entered\" callback properly fires when a location outside the GeoQuery moves somewhere within the GeoQuery that is already occupied by another key", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc1 entered", "loc3 entered"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - - geoFire.set({ - "loc1": [50, 50], - "loc2": [50, 50], - "loc3": [0, 0] - }).then(function() { - cl.x("p1"); - - return geoFire.set("loc1", [0, 0]); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("multiple \"key_entered\" callbacks fire when a location enters the GeoQuery", function(done) { - var cl = new Checklist(["p1", "p2", "loc1 entered1", "loc4 entered1", "loc1 entered2", "loc4 entered2"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered1"); - }); - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered2"); - }); - - geoFire.set({ - "loc1": [2, 3], - "loc2": [50, -7], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [67, 55] - }).then(function() { - cl.x("p1"); - - return wait(100); - }).then(function() { - cl.x("p2"); - }).catch(failTestOnCaughtError); - }); - }); - - describe("\"key_exited\" event:", function() { - it("\"key_exited\" callback fires when a location leaves the GeoQuery", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc1 exited", "loc4 exited"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited"); - }); - - geoFire.set({ - "loc1": [2, 3], - "loc2": [50, -7], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [67, 55] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [25, 90], - "loc4": [25, 5] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_exited\" callback gets passed correct location parameter", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc1 exited to 25,90", "loc4 exited to 25,5"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited to " + location); - }); - - geoFire.set({ - "loc1": [2, 3], - "loc2": [5, 2], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [67, 55] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [25, 90], - "loc2": [5, 5], - "loc4": [25, 5] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_exited\" callback gets passed correct distance parameter", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc1 exited (9759.01 km from center)", "loc4 exited (2688.06 km from center)"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited (" + distance.toFixed(2) + " km from center)"); - }); - - geoFire.set({ - "loc1": [2, 3], - "loc2": [5, 2], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [67, 55] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [25, 90], - "loc2": [5, 5], - "loc4": [25, 5] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_exited\" callback gets passed null for location and distance parameters if the key is entirely removed from GeoFire", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc1 exited"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_exited", function(key, location, distance) { - expect(location).toBeNull(); - expect(distance).toBeNull(); - cl.x(key + " exited"); - }); - - geoFire.set("loc1", [2, 3]).then(function() { - cl.x("p1"); - - return geoFire.remove("loc1"); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_exited\" callback fires when a location within the GeoQuery is entirely removed from GeoFire", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc1 exited"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [2, 3] - }).then(function() { - cl.x("p1"); - - return geoFire.remove("loc1"); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_exited\" callback properly fires when multiple keys are at the same location inside the GeoQuery and only one of them moves outside the GeoQuery", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc1 exited"], expect, done); - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [0, 0], - "loc3": [18, -121] - }).then(function() { - cl.x("p1"); - - return geoFire.set("loc1", [20, -55]); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("\"key_exited\" callback properly fires when a location inside the GeoQuery moves somewhere outside the GeoQuery that is already occupied by another key", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc1 exited"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited"); - }); - - geoFire.set({ - "loc1": [0, 0], - "loc2": [50, 50], - "loc3": [18, -121] - }).then(function() { - cl.x("p1"); - - return geoFire.set("loc1", [18, -121]); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - - it("multiple \"key_exited\" callbacks fire when a location leaves the GeoQuery", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "loc1 exited1", "loc4 exited1", "loc1 exited2", "loc4 exited2"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited1"); - }); - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited2"); - }); - - geoFire.set({ - "loc1": [2, 3], - "loc2": [50, -7], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [67, 55] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [25, 90], - "loc4": [25, 5] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3"); - }).catch(failTestOnCaughtError); - }); - }); - - describe("\"key_*\" events combined:", function() { - it ("\"key_*\" event callbacks fire when used all at the same time", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "loc1 entered", "loc4 entered", "loc1 moved", "loc4 exited", "loc1 exited", "loc5 entered"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited"); - }); - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved"); - }); - - geoFire.set({ - "loc1": [2, 3], - "loc2": [50, -7], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [67, 55] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [1, 1], - "loc4": [25, 5] - }); - }).then(function() { - cl.x("p2"); - - return geoFire.set({ - "loc1": [10, -100], - "loc2": [50, -50], - "loc5": [5, 5] - }); - }).then(function() { - cl.x("p3"); - - return wait(100); - }).then(function() { - cl.x("p4"); - }).catch(failTestOnCaughtError); - }); - - it ("location moving between geohash queries triggers a key_moved", function(done) { - var cl = new Checklist(["loc1 entered", "loc2 entered", "p1", "loc1 moved", "loc2 moved", "p2"], expect, done); - - geoQueries.push(geoFire.query({center: [0,0], radius: 1000})); - - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited"); - }); - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved"); - }); - - geoFire.set({ - "loc1": [-1, -1], - "loc2": [1, 1] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [1, 1], - "loc2": [-1, -1] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).catch(failTestOnCaughtError); - }); - }); - - describe("Cancelling GeoQuery:", function() { - it("cancel() prevents GeoQuery from firing any more \"key_*\" event callbacks", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "p5", "loc1 entered", "loc4 entered", "loc1 moved", "loc4 exited"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited"); - }); - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved"); - }); - - geoFire.set({ - "loc1": [2, 3], - "loc2": [50, -7], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [67, 55] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [1, 1], - "loc4": [25, 5] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3") - - geoQueries[0].cancel(); - - return wait(1000); - }).then(function() { - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered"); - }); - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited"); - }); - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved"); - }); - - return geoFire.set({ - "loc1": [10, -100], - "loc2": [50, -50], - "loc5": [5, 5] - }); - }).then(function() { - cl.x("p4"); - - return wait(100); - }).then(function() { - cl.x("p5"); - }).catch(failTestOnCaughtError); - }); - - it("Calling cancel() on one GeoQuery does not cancel other GeoQueries", function(done) { - var cl = new Checklist(["p1", "p2", "p3", "p4", "p5", "loc1 entered1", "loc1 entered2", "loc4 entered1", "loc4 entered2", "loc1 moved1", "loc1 moved2", "loc4 exited1", "loc4 exited2", "loc1 exited2", "loc5 entered2"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x(key + " entered1"); - }); - geoQueries[0].on("key_exited", function(key, location, distance) { - cl.x(key + " exited1"); - }); - geoQueries[0].on("key_moved", function(key, location, distance) { - cl.x(key + " moved1"); - }); - - geoQueries[1].on("key_entered", function(key, location, distance) { - cl.x(key + " entered2"); - }); - geoQueries[1].on("key_exited", function(key, location, distance) { - cl.x(key + " exited2"); - }); - geoQueries[1].on("key_moved", function(key, location, distance) { - cl.x(key + " moved2"); - }); - - geoFire.set({ - "loc1": [2, 3], - "loc2": [50, -7], - "loc3": [16, -150], - "loc4": [5, 5], - "loc5": [67, 55] - }).then(function() { - cl.x("p1"); - - return geoFire.set({ - "loc1": [1, 1], - "loc4": [25, 5] - }); - }).then(function() { - cl.x("p2"); - - return wait(100); - }).then(function() { - cl.x("p3") - - geoQueries[0].cancel(); - - return geoFire.set({ - "loc1": [10, -100], - "loc2": [50, -50], - "loc5": [1, 2] - }); - }).then(function() { - cl.x("p4"); - - return wait(100); - }).then(function() { - cl.x("p5"); - }).catch(failTestOnCaughtError); - }); - - it("Calling cancel() in the middle of firing \"key_entered\" events is allowed", function(done) { - var cl = new Checklist(["p1", "key entered", "cancel query"], expect, done); - - geoQueries.push(geoFire.query({center: [1,2], radius: 1000})); - - geoFire.set({ - "loc1": [1, 2], - "loc2": [1, 3], - "loc3": [1, 4] - }).then(function() { - cl.x("p1"); - - var numKeyEnteredEventsFired = 0; - geoQueries[0].on("key_entered", function(key, location, distance) { - cl.x("key entered"); - numKeyEnteredEventsFired++; - if (numKeyEnteredEventsFired === 1) { - cl.x("cancel query"); - geoQueries[0].cancel(); - } - }); - }).catch(failTestOnCaughtError); - }); - }); -}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..6eb2e8f3 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "outDir": "./dist", + "baseUrl": ".", + "sourceMap": false, + "declaration": true, + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "es5", + "typeRoots": [ + "node_modules/@types" + ], + "lib": [ + "es2016", + "dom" + ] + }, + "files": [ + "./src/index.ts" + ] +} \ No newline at end of file From 41dff26012d7adfe7d5dca936e8dc122e8115349 Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Thu, 8 Mar 2018 21:23:57 -0500 Subject: [PATCH 02/23] Fix additional mocha tests --- .travis.yml | 6 +- src/firebase/geoCallbackRegistration.ts | 2 +- tests/common.ts | 59 ++-- tests/geoCallbackRegistration.test.ts | 50 +-- tests/geoFire.test.ts | 404 ++++++++++++------------ tests/geoFireUtils.test.ts | 98 +++--- tests/geoQuery.test.ts | 346 ++++++++++---------- 7 files changed, 480 insertions(+), 485 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9159680a..767481e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,7 @@ language: node_js node_js: - - '0.12' - #- stable + - stable sudo: false -before_install: - - export DISPLAY=:99.0 - - sh -e /etc/init.d/xvfb start install: - npm install script: diff --git a/src/firebase/geoCallbackRegistration.ts b/src/firebase/geoCallbackRegistration.ts index 9e25697c..773b9996 100644 --- a/src/firebase/geoCallbackRegistration.ts +++ b/src/firebase/geoCallbackRegistration.ts @@ -6,7 +6,7 @@ export class GeoCallbackRegistration { * @param _cancelCallback Callback to run when this callback registration is cancelled. */ constructor(private _cancelCallback: Function) { - if (typeof this._cancelCallback !== 'function') { + if (Object.prototype.toString.call(this._cancelCallback) !== '[object Function]') { throw new Error('callback must be a function'); } } diff --git a/tests/common.ts b/tests/common.ts index 151b0f3d..1c476a79 100644 --- a/tests/common.ts +++ b/tests/common.ts @@ -8,21 +8,21 @@ import { GeoFire } from '../src'; /*************/ const expect = chai.expect; // Define examples of valid and invalid parameters -export var invalidFirebaseRefs = [null, undefined, NaN, true, false, [], 0, 5, '', 'a', ['hi', 1]]; -export var validKeys = ['a', 'loc1', '(e@Xi:4t>*E2)hc<5oa:1s6{B0d?u', Array(700).join('a')]; -export var invalidKeys = ['', true, false, null, undefined, {a: 1}, 'loc.1', 'loc$1', '[loc1', 'loc1]', 'loc#1', 'loc/1', 'a#i]$da[s', 'te/nst', 'te/rst', 'te/u0000st', 'te/u0015st', 'te/007Fst', Array(800).join('a')]; -export var validLocations = [[0, 0], [-90, 180], [90, -180], [23, 74], [47.235124363, 127.2379654226]]; -export var invalidLocations = [[-91, 0], [91, 0], [0, 181], [0, -181], [[0, 0], 0], ['a', 0], [0, 'a'], ['a', 'a'], [NaN, 0], [0, NaN], [undefined, NaN], [null, 0], [null, null], [0, undefined], [undefined, undefined], '', 'a', true, false, [], [1], {}, {a:1}, null, undefined, NaN]; -export var validGeohashes = ['4', 'd62dtu', '000000000000']; -export var invalidGeohashes = ['', 'aaa', 1, true, false, [], [1], {}, {a:1}, null, undefined, NaN]; -export var validQueryCriterias = [{center: [0,0], radius: 1000}, {center: [1,-180], radius: 1.78}, {center: [22.22,-107.77], radius: 0}, {center: [0,0]}, {center: [1,-180]}, {center: [22.22,-107.77]}, {radius: 1000}, {radius: 1.78}, {radius: 0}]; -export var invalidQueryCriterias = [{}, {random: 100}, {center: [91,2], radius: 1000, random: 'a'}, {center: [91,2], radius: 1000}, {center: [1,-181], radius: 1000}, {center: ['a',2], radius: 1000}, {center: [1,[1,2]], radius: 1000}, {center: [0,0], radius: -1}, {center: [null,2], radius: 1000}, {center: [1,undefined], radius: 1000}, {center: [NaN,0], radius: 1000}, {center: [1,2], radius: -10}, {center: [1,2], radius: 'text'}, {center: [1,2], radius: [1,2]}, {center: [1,2], radius: null}, true, false, undefined, NaN, [], 'a', 1]; - -// Create global variables to hold the Firebase and GeoFire variables -export var geoFireRef, geoFire, geoQueries = []; +export const invalidFirebaseRefs = [null, undefined, NaN, true, false, [], 0, 5, '', 'a', ['hi', 1]]; +export const validKeys = ['a', 'loc1', '(e@Xi:4t>*E2)hc<5oa:1s6{B0d?u', Array(700).join('a')]; +export const invalidKeys = ['', true, false, null, undefined, { a: 1 }, 'loc.1', 'loc$1', '[loc1', 'loc1]', 'loc#1', 'loc/1', 'a#i]$da[s', 'te/nst', 'te/rst', 'te/u0000st', 'te/u0015st', 'te/007Fst', Array(800).join('a')]; +export const validLocations = [[0, 0], [-90, 180], [90, -180], [23, 74], [47.235124363, 127.2379654226]]; +export const invalidLocations = [[-91, 0], [91, 0], [0, 181], [0, -181], [[0, 0], 0], ['a', 0], [0, 'a'], ['a', 'a'], [NaN, 0], [0, NaN], [undefined, NaN], [null, 0], [null, null], [0, undefined], [undefined, undefined], '', 'a', true, false, [], [1], {}, { a: 1 }, null, undefined, NaN]; +export const validGeohashes = ['4', 'd62dtu', '000000000000']; +export const invalidGeohashes = ['', 'aaa', 1, true, false, [], [1], {}, { a: 1 }, null, undefined, NaN]; +export const validQueryCriterias = [{ center: [0, 0], radius: 1000 }, { center: [1, -180], radius: 1.78 }, { center: [22.22, -107.77], radius: 0 }, { center: [0, 0] }, { center: [1, -180] }, { center: [22.22, -107.77] }, { radius: 1000 }, { radius: 1.78 }, { radius: 0 }]; +export const invalidQueryCriterias = [{}, { random: 100 }, { center: [91, 2], radius: 1000, random: 'a' }, { center: [91, 2], radius: 1000 }, { center: [1, -181], radius: 1000 }, { center: ['a', 2], radius: 1000 }, { center: [1, [1, 2]], radius: 1000 }, { center: [0, 0], radius: -1 }, { center: [null, 2], radius: 1000 }, { center: [1, undefined], radius: 1000 }, { center: [NaN, 0], radius: 1000 }, { center: [1, 2], radius: -10 }, { center: [1, 2], radius: 'text' }, { center: [1, 2], radius: [1, 2] }, { center: [1, 2], radius: null }, true, false, undefined, NaN, [], 'a', 1]; + +// Create global constiables to hold the Firebase and GeoFire constiables +export let geoFireRef, geoFire, geoQueries = []; // Initialize Firebase -var config = { +const config = { apiKey: 'AIzaSyC5IcRccDo289TTRa3Y7qJIu8YPz3EnKAI', databaseURL: 'https://geofire-9d0de.firebaseio.com' }; @@ -35,7 +35,6 @@ firebase.initializeApp(config); export function beforeEachHelper(done) { // Create a new Firebase database ref at a random node geoFireRef = firebase.database().ref().push(); - // Create a new GeoFire instance geoFire = new GeoFire(geoFireRef); @@ -48,23 +47,23 @@ export function beforeEachHelper(done) { /* Helper function which runs after each Jasmine test has completed */ export function afterEachHelper(done) { // Cancel each outstanding GeoQuery - geoQueries.forEach(function(geoQuery) { + geoQueries.forEach(function (geoQuery) { geoQuery.cancel(); }) - geoFireRef.remove().then(function() { + geoFireRef.remove().then(function () { // Wait for 50 milliseconds after each test to give enough time for old query events to expire return wait(50); }).then(done); } -/* Returns a random alphabetic string of variable length */ +/* Returns a random alphabetic string of constiable length */ export function generateRandomString() { - var possibleCharacters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - var numPossibleCharacters = possibleCharacters.length; + const possibleCharacters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + const numPossibleCharacters = possibleCharacters.length; - var text = ''; - for (var i = 0; i < 10; i++) { + let text = ''; + for (let i = 0; i < 10; i++) { text += possibleCharacters.charAt(Math.floor(Math.random() * numPossibleCharacters)); } @@ -73,7 +72,7 @@ export function generateRandomString() { /* Returns the current data in the Firebase */ export function getFirebaseData() { - return geoFireRef.once('value').then(function(dataSnapshot) { + return geoFireRef.once('value').then(function (dataSnapshot) { return dataSnapshot.exportVal(); }); }; @@ -81,8 +80,8 @@ export function getFirebaseData() { /* Returns a promise which is fulfilled after the inputted number of milliseconds pass */ export function wait(milliseconds) { - return new Promise(function(resolve) { - var timeout = window.setTimeout(function() { + return new Promise(function (resolve) { + const timeout = window.setTimeout(function () { window.clearTimeout(timeout); resolve(); }, milliseconds); @@ -91,11 +90,11 @@ export function wait(milliseconds) { /* Keeps track of all the current asynchronous tasks being run */ export function Checklist(items, expect, done) { - var eventsToComplete = items; + const eventsToComplete = items; /* Removes a task from the events list */ - this.x = function(item) { - var index = eventsToComplete.indexOf(item); + this.x = function (item) { + const index = eventsToComplete.indexOf(item); if (index === -1) { expect('Attempting to delete unexpected item \'' + item + '\' from Checklist').toBeFalsy(); } @@ -108,12 +107,12 @@ export function Checklist(items, expect, done) { }; /* Returns the length of the events list */ - this.length = function() { + this.length = function () { return eventsToComplete.length; }; /* Returns true if the events list is empty */ - this.isEmpty = function() { + this.isEmpty = function () { return (this.length() === 0); }; }; @@ -124,5 +123,5 @@ export function Checklist(items, expect, done) { * went wrong. **/ export function failTestOnCaughtError(error) { - expect(error).to.equal(null); + expect(error).to.throw(); } diff --git a/tests/geoCallbackRegistration.test.ts b/tests/geoCallbackRegistration.test.ts index 46f139c0..892e8079 100644 --- a/tests/geoCallbackRegistration.test.ts +++ b/tests/geoCallbackRegistration.test.ts @@ -20,22 +20,22 @@ describe('GeoCallbackRegistration Tests:', () => { describe('Constructor:', () => { it('Constructor throws error given non-function', () => { - var createCallbackRegistration = () => { - // @ts-ignore + const createCallbackRegistration = () => { + // @ts-ignore new GeoCallbackRegistration('nonFunction'); } - expect(() => createCallbackRegistration).to.throw(); + expect(() => createCallbackRegistration()).to.throw(null, 'callback must be a function'); }); }); describe('Cancelling event callbacks:', () => { - it('\'key_moved\' registrations can be cancelled', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 moved'], expect, done); + it('\'key_moved\' registrations can be cancelled', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 moved'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - var onKeyMovedRegistration = geoQueries[0].on('key_moved', function (key, location, distance) { + const onKeyMovedRegistration = geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -65,12 +65,12 @@ describe('GeoCallbackRegistration Tests:', () => { }).catch(failTestOnCaughtError);; }); - it('\'key_entered\' registrations can be cancelled', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered'], expect, done); + it('\'key_entered\' registrations can be cancelled', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - var onKeyEnteredRegistration = geoQueries[0].on('key_entered', function (key, location, distance) { + const onKeyEnteredRegistration = geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -96,12 +96,12 @@ describe('GeoCallbackRegistration Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_exited\' registrations can be cancelled', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 exited'], expect, done); + it('\'key_exited\' registrations can be cancelled', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 exited'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - var onKeyExitedRegistration = geoQueries[0].on('key_exited', function (key, location, distance) { + const onKeyExitedRegistration = geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -131,15 +131,15 @@ describe('GeoCallbackRegistration Tests:', () => { }).catch(failTestOnCaughtError); }); - it('Cancelling a \'key_moved\' registration does not cancel all \'key_moved\' callbacks', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 moved1', 'loc1 moved2', 'loc3 moved2'], expect, done); + it('Cancelling a \'key_moved\' registration does not cancel all \'key_moved\' callbacks', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 moved1', 'loc1 moved2', 'loc3 moved2'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - var onKeyMovedRegistration1 = geoQueries[0].on('key_moved', function (key, location, distance) { + const onKeyMovedRegistration1 = geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved1'); }); - var onKeyMovedRegistration2 = geoQueries[0].on('key_moved', function (key, location, distance) { + const onKeyMovedRegistration2 = geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved2'); }); @@ -169,15 +169,15 @@ describe('GeoCallbackRegistration Tests:', () => { }).catch(failTestOnCaughtError); }); - it('Cancelling a \'key_entered\' registration does not cancel all \'key_entered\' callbacks', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered1', 'loc1 entered2', 'loc3 entered2'], expect, done); + it('Cancelling a \'key_entered\' registration does not cancel all \'key_entered\' callbacks', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered1', 'loc1 entered2', 'loc3 entered2'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - var onKeyEnteredRegistration1 = geoQueries[0].on('key_entered', function (key, location, distance) { + const onKeyEnteredRegistration1 = geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered1'); }); - var onKeyEnteredRegistration2 = geoQueries[0].on('key_entered', function (key, location, distance) { + const onKeyEnteredRegistration2 = geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered2'); }); @@ -203,15 +203,15 @@ describe('GeoCallbackRegistration Tests:', () => { }).catch(failTestOnCaughtError); }); - it('Cancelling a \'key_exited\' registration does not cancel all \'key_exited\' callbacks', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 exited1', 'loc1 exited2', 'loc3 exited2'], expect, done); + it('Cancelling a \'key_exited\' registration does not cancel all \'key_exited\' callbacks', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 exited1', 'loc1 exited2', 'loc3 exited2'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - var onKeyExitedRegistration1 = geoQueries[0].on('key_exited', function (key, location, distance) { + const onKeyExitedRegistration1 = geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited1'); }); - var onKeyExitedRegistration2 = geoQueries[0].on('key_exited', function (key, location, distance) { + const onKeyExitedRegistration2 = geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited2'); }); @@ -244,7 +244,7 @@ describe('GeoCallbackRegistration Tests:', () => { it('Calling cancel on a GeoCallbackRegistration twice does not throw', () => { geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - var onKeyExitedRegistration = geoQueries[0].on('key_exited', () => { }); + const onKeyExitedRegistration = geoQueries[0].on('key_exited', () => { }); expect(() => onKeyExitedRegistration.cancel()).not.throw(); expect(() => onKeyExitedRegistration.cancel()).not.throw(); diff --git a/tests/geoFire.test.ts b/tests/geoFire.test.ts index 537b4fd2..35743db1 100755 --- a/tests/geoFire.test.ts +++ b/tests/geoFire.test.ts @@ -9,61 +9,61 @@ import { const expect = chai.expect; -describe('GeoFire Tests:', function () { +describe('GeoFire Tests:', () => { // Reset the Firebase before each test - beforeEach(function (done) { + beforeEach((done) => { beforeEachHelper(done); }); - afterEach(function (done) { + afterEach((done) => { afterEachHelper(done); }); - describe('Constructor:', function () { - it('Constructor throws errors given invalid Firebase references', function () { - invalidFirebaseRefs.forEach(function (invalidFirebaseRef) { + describe('Constructor:', () => { + it('Constructor throws errors given invalid Firebase references', () => { + invalidFirebaseRefs.forEach((invalidFirebaseRef) => { // @ts-ignore - expect(function () { new GeoFire(invalidFirebaseRef); }).to.throw(); + expect(() => new GeoFire(invalidFirebaseRef)).to.throw(null, 'firebaseRef must be an instance of Firebase'); }); }); - it('Constructor does not throw errors given valid Firebase references', function () { - expect(function () { new GeoFire(geoFireRef); }).not.to.throw(); + it('Constructor does not throw errors given valid Firebase references', () => { + expect(() => new GeoFire(geoFireRef)).not.to.throw(); }); }); - describe('ref():', function () { - it('ref() returns the Firebase reference used to create a GeoFire instance', function () { + describe('ref():', () => { + it('ref() returns the Firebase reference used to create a GeoFire instance', () => { expect(geoFire.ref()).to.deep.equal(geoFireRef); }); }); - describe('Adding a single location via set():', function () { - it('set() returns a promise', function (done) { + describe('Adding a single location via set():', () => { + it('set() returns a promise', (done) => { - var cl = new Checklist(['p1'], expect, done); + const cl = new Checklist(['p1'], expect, done); - geoFire.set('loc1', [0, 0]).then(function () { + geoFire.set('loc1', [0, 0]).then(() => { cl.x('p1'); }); }); - it('set() updates Firebase when adding new locations', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); + it('set() updates Firebase when adding new locations', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); - geoFire.set('loc1', [0, 0]).then(function () { + geoFire.set('loc1', [0, 0]).then(() => { cl.x('p1'); return geoFire.set('loc2', [50, 50]); - }).then(function () { + }).then(() => { cl.x('p2'); return geoFire.set('loc3', [-90, -90]); - }).then(function () { + }).then(() => { cl.x('p3'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' }, 'loc2': { '.priority': 'v0gs3y0zh7', 'l': { '0': 50, '1': 50 }, 'g': 'v0gs3y0zh7' }, @@ -74,22 +74,22 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('set() handles decimal latitudes and longitudes', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); + it('set() handles decimal latitudes and longitudes', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); - geoFire.set('loc1', [0.254, 0]).then(function () { + geoFire.set('loc1', [0.254, 0]).then(() => { cl.x('p1'); return geoFire.set('loc2', [50, 50.293403]); - }).then(function () { + }).then(() => { cl.x('p2'); return geoFire.set('loc3', [-82.614, -90.938]); - }).then(function () { + }).then(() => { cl.x('p3'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc1': { '.priority': 'ebpcrypzxv', 'l': { '0': 0.254, '1': 0 }, 'g': 'ebpcrypzxv' }, 'loc2': { '.priority': 'v0gu2qnx15', 'l': { '0': 50, '1': 50.293403 }, 'g': 'v0gu2qnx15' }, @@ -100,26 +100,26 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('set() updates Firebase when changing a pre-existing key', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + it('set() updates Firebase when changing a pre-existing key', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); - geoFire.set('loc1', [0, 0]).then(function () { + geoFire.set('loc1', [0, 0]).then(() => { cl.x('p1'); return geoFire.set('loc2', [50, 50]); - }).then(function () { + }).then(() => { cl.x('p2'); return geoFire.set('loc3', [-90, -90]); - }).then(function () { + }).then(() => { cl.x('p3'); return geoFire.set('loc1', [2, 3]); - }).then(function () { + }).then(() => { cl.x('p4'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc1': { '.priority': 's065kk0dc5', 'l': { '0': 2, '1': 3 }, 'g': 's065kk0dc5' }, 'loc2': { '.priority': 'v0gs3y0zh7', 'l': { '0': 50, '1': 50 }, 'g': 'v0gs3y0zh7' }, @@ -130,26 +130,26 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('set() updates Firebase when changing a pre-existing key to the same location', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + it('set() updates Firebase when changing a pre-existing key to the same location', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); - geoFire.set('loc1', [0, 0]).then(function () { + geoFire.set('loc1', [0, 0]).then(() => { cl.x('p1'); return geoFire.set('loc2', [50, 50]); - }).then(function () { + }).then(() => { cl.x('p2'); return geoFire.set('loc3', [-90, -90]); - }).then(function () { + }).then(() => { cl.x('p3'); return geoFire.set('loc1', [0, 0]); - }).then(function () { + }).then(() => { cl.x('p4'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' }, 'loc2': { '.priority': 'v0gs3y0zh7', 'l': { '0': 50, '1': 50 }, 'g': 'v0gs3y0zh7' }, @@ -160,22 +160,22 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('set() handles multiple keys at the same location', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); + it('set() handles multiple keys at the same location', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); - geoFire.set('loc1', [0, 0]).then(function () { + geoFire.set('loc1', [0, 0]).then(() => { cl.x('p1'); return geoFire.set('loc2', [0, 0]); - }).then(function () { + }).then(() => { cl.x('p2'); return geoFire.set('loc3', [0, 0]); - }).then(function () { + }).then(() => { cl.x('p3'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' }, 'loc2': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' }, @@ -186,50 +186,50 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('set() updates Firebase after complex operations', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9', 'p10', 'p11'], expect, done); + it('set() updates Firebase after complex operations', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9', 'p10', 'p11'], expect, done); - geoFire.set('loc:1', [0, 0]).then(function () { + geoFire.set('loc:1', [0, 0]).then(() => { cl.x('p1'); return geoFire.set('loc2', [50, 50]); - }).then(function () { + }).then(() => { cl.x('p2'); return geoFire.set('loc%!A72f()3', [-90, -90]); - }).then(function () { + }).then(() => { cl.x('p3'); return geoFire.remove('loc2'); - }).then(function () { + }).then(() => { cl.x('p4'); return geoFire.set('loc2', [0.2358, -72.621]); - }).then(function () { + }).then(() => { cl.x('p5'); return geoFire.set('loc4', [87.6, -130]); - }).then(function () { + }).then(() => { cl.x('p6'); return geoFire.set('loc5', [5, 55.555]); - }).then(function () { + }).then(() => { cl.x('p7'); return geoFire.set('loc5', null); - }).then(function () { + }).then(() => { cl.x('p8'); return geoFire.set('loc:1', [87.6, -130]); - }).then(function () { + }).then(() => { cl.x('p9'); return geoFire.set('loc6', [-72.258, 0.953215]); - }).then(function () { + }).then(() => { cl.x('p10'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc:1': { '.priority': 'cped3g0fur', 'l': { '0': 87.6, '1': -130 }, 'g': 'cped3g0fur' }, 'loc2': { '.priority': 'd2h376zj8h', 'l': { '0': 0.2358, '1': -72.621 }, 'g': 'd2h376zj8h' }, @@ -242,35 +242,35 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('set() does not throw errors given valid keys', function () { - validKeys.forEach(function (validKey) { - expect(function () { + it('set() does not throw errors given valid keys', () => { + validKeys.forEach((validKey) => { + expect(() => { geoFire.set(validKey, [0, 0]); }).not.to.throw(); }); }); - it('set() throws errors given invalid keys', function () { - invalidKeys.forEach(function (invalidKey) { - expect(function () { + it('set() throws errors given invalid keys', () => { + invalidKeys.forEach((invalidKey) => { + expect(() => { geoFire.set(invalidKey, [0, 0]); }).to.throw(); }); }); - it('set() does not throw errors given valid locations', function () { - validLocations.forEach(function (validLocation, i) { - expect(function () { + it('set() does not throw errors given valid locations', () => { + validLocations.forEach((validLocation, i) => { + expect(() => { geoFire.set('loc', validLocation); }).not.to.throw(); }); }); - it('set() throws errors given invalid locations', function () { - invalidLocations.forEach(function (invalidLocation, i) { + it('set() throws errors given invalid locations', () => { + invalidLocations.forEach((invalidLocation, i) => { // Setting location to null is valid since it will remove the key if (invalidLocation !== null) { - expect(function () { + expect(() => { geoFire.set('loc', invalidLocation); }).to.throw(); } @@ -278,30 +278,30 @@ describe('GeoFire Tests:', function () { }); }); - describe('Adding multiple locations via set():', function () { - it('set() returns a promise', function (done) { + describe('Adding multiple locations via set():', () => { + it('set() returns a promise', (done) => { - var cl = new Checklist(['p1'], expect, done); + const cl = new Checklist(['p1'], expect, done); geoFire.set({ 'loc1': [0, 0] - }).then(function () { + }).then(() => { cl.x('p1'); }); }); - it('set() updates Firebase when adding new locations', function (done) { - var cl = new Checklist(['p1', 'p2'], expect, done); + it('set() updates Firebase when adding new locations', (done) => { + const cl = new Checklist(['p1', 'p2'], expect, done); geoFire.set({ 'loc1': [0, 0], 'loc2': [50, 50], 'loc3': [-90, -90] - }).then(function () { + }).then(() => { cl.x('p1'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' }, 'loc2': { '.priority': 'v0gs3y0zh7', 'l': { '0': 50, '1': 50 }, 'g': 'v0gs3y0zh7' }, @@ -312,18 +312,18 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('set() handles decimal latitudes and longitudes', function (done) { - var cl = new Checklist(['p1', 'p2'], expect, done); + it('set() handles decimal latitudes and longitudes', (done) => { + const cl = new Checklist(['p1', 'p2'], expect, done); geoFire.set({ 'loc1': [0.254, 0], 'loc2': [50, 50.293403], 'loc3': [-82.614, -90.938] - }).then(function () { + }).then(() => { cl.x('p1'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc1': { '.priority': 'ebpcrypzxv', 'l': { '0': 0.254, '1': 0 }, 'g': 'ebpcrypzxv' }, 'loc2': { '.priority': 'v0gu2qnx15', 'l': { '0': 50, '1': 50.293403 }, 'g': 'v0gu2qnx15' }, @@ -334,24 +334,24 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('set() updates Firebase when changing a pre-existing key', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + it('set() updates Firebase when changing a pre-existing key', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); geoFire.set({ 'loc1': [0, 0], 'loc2': [50, 50], 'loc3': [-90, -90] - }).then(function () { + }).then(() => { cl.x('p1'); return geoFire.set({ 'loc1': [2, 3] }); - }).then(function () { + }).then(() => { cl.x('p2'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc1': { '.priority': 's065kk0dc5', 'l': { '0': 2, '1': 3 }, 'g': 's065kk0dc5' }, 'loc2': { '.priority': 'v0gs3y0zh7', 'l': { '0': 50, '1': 50 }, 'g': 'v0gs3y0zh7' }, @@ -362,24 +362,24 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('set() updates Firebase when changing a pre-existing key to the same location', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + it('set() updates Firebase when changing a pre-existing key to the same location', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); geoFire.set({ 'loc1': [0, 0], 'loc2': [50, 50], 'loc3': [-90, -90] - }).then(function () { + }).then(() => { cl.x('p1'); return geoFire.set({ 'loc1': [0, 0] }); - }).then(function () { + }).then(() => { cl.x('p2'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' }, 'loc2': { '.priority': 'v0gs3y0zh7', 'l': { '0': 50, '1': 50 }, 'g': 'v0gs3y0zh7' }, @@ -390,18 +390,18 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('set() handles multiple keys at the same location', function (done) { - var cl = new Checklist(['p1', 'p2'], expect, done); + it('set() handles multiple keys at the same location', (done) => { + const cl = new Checklist(['p1', 'p2'], expect, done); geoFire.set({ 'loc1': [0, 0], 'loc2': [0, 0], 'loc3': [0, 0] - }).then(function () { + }).then(() => { cl.x('p1'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' }, 'loc2': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' }, @@ -412,18 +412,18 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('set() updates Firebase after complex operations', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6'], expect, done); + it('set() updates Firebase after complex operations', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6'], expect, done); geoFire.set({ 'loc:1': [0, 0], 'loc2': [50, 50], 'loc%!A72f()3': [-90, -90] - }).then(function () { + }).then(() => { cl.x('p1'); return geoFire.remove('loc2'); - }).then(function () { + }).then(() => { cl.x('p2'); return geoFire.set({ @@ -431,24 +431,24 @@ describe('GeoFire Tests:', function () { 'loc4': [87.6, -130], 'loc5': [5, 55.555] }); - }).then(function () { + }).then(() => { cl.x('p3'); return geoFire.set({ 'loc5': null }); - }).then(function () { + }).then(() => { cl.x('p4'); return geoFire.set({ 'loc:1': [87.6, -130], 'loc6': [-72.258, 0.953215] }); - }).then(function () { + }).then(() => { cl.x('p5'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc:1': { '.priority': 'cped3g0fur', 'l': { '0': 87.6, '1': -130 }, 'g': 'cped3g0fur' }, 'loc2': { '.priority': 'd2h376zj8h', 'l': { '0': 0.2358, '1': -72.621 }, 'g': 'd2h376zj8h' }, @@ -461,21 +461,21 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('set() does not throw errors given valid keys', function () { - validKeys.forEach(function (validKey) { - expect(function () { - var locations = {}; + it('set() does not throw errors given valid keys', () => { + validKeys.forEach((validKey) => { + expect(() => { + const locations = {}; locations[validKey] = [0, 0]; geoFire.set(locations); }).not.to.throw(); }); }); - it('set() throws errors given invalid keys', function () { - invalidKeys.forEach(function (invalidKey) { + it('set() throws errors given invalid keys', () => { + invalidKeys.forEach((invalidKey) => { if (invalidKey !== null && invalidKey !== undefined && typeof invalidKey !== 'boolean') { - expect(function () { - var locations = {}; + expect(() => { + const locations = {}; // @ts-ignore locations[invalidKey] = [0, 0]; geoFire.set(locations); @@ -484,17 +484,17 @@ describe('GeoFire Tests:', function () { }); }); - it('set() throws errors given a location argument in combination with an object', function () { - expect(function () { + it('set() throws errors given a location argument in combination with an object', () => { + expect(() => { geoFire.set({ 'loc': [0, 0] }, [0, 0]); }).to.throw(); }); - it('set() does not throw errors given valid locations', function () { - validLocations.forEach(function (validLocation, i) { - expect(function () { + it('set() does not throw errors given valid locations', () => { + validLocations.forEach((validLocation, i) => { + expect(() => { geoFire.set({ 'loc': validLocation }); @@ -502,11 +502,11 @@ describe('GeoFire Tests:', function () { }); }); - it('set() throws errors given invalid locations', function () { - invalidLocations.forEach(function (invalidLocation, i) { + it('set() throws errors given invalid locations', () => { + invalidLocations.forEach((invalidLocation, i) => { // Setting location to null is valid since it will remove the key if (invalidLocation !== null) { - expect(function () { + expect(() => { geoFire.set({ 'loc': invalidLocation }); @@ -516,93 +516,93 @@ describe('GeoFire Tests:', function () { }); }); - describe('Retrieving locations:', function () { - it('get() returns a promise', function (done) { - var cl = new Checklist(['p1'], expect, done); + describe('Retrieving locations:', () => { + it('get() returns a promise', (done) => { + const cl = new Checklist(['p1'], expect, done); - geoFire.get('loc1').then(function () { + geoFire.get('loc1').then(() => { cl.x('p1'); }); }); - it('get() returns null for non-existent keys', function (done) { - var cl = new Checklist(['p1'], expect, done); + it('get() returns null for non-existent keys', (done) => { + const cl = new Checklist(['p1'], expect, done); - geoFire.get('loc1').then(function (location) { + geoFire.get('loc1').then((location) => { expect(location).to.equal(null); cl.x('p1'); }); }); - it('get() retrieves locations given existing keys', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); + it('get() retrieves locations given existing keys', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); geoFire.set({ 'loc1': [0, 0], 'loc2': [50, 50], 'loc3': [-90, -90] - }).then(function () { + }).then(() => { cl.x('p1'); return geoFire.get('loc1'); - }).then(function (location) { + }).then((location) => { expect(location).to.deep.equal([0, 0]); cl.x('p2'); return geoFire.get('loc2'); - }).then(function (location) { + }).then((location) => { expect(location).to.deep.equal([50, 50]); cl.x('p3'); return geoFire.get('loc3'); - }).then(function (location) { + }).then((location) => { expect(location).to.deep.equal([-90, -90]); cl.x('p4'); }).catch(failTestOnCaughtError); }); - it('get() does not throw errors given valid keys', function () { - validKeys.forEach(function (validKey) { - expect(function () { geoFire.get(validKey); }).not.to.throw(); + it('get() does not throw errors given valid keys', () => { + validKeys.forEach((validKey) => { + expect(() => geoFire.get(validKey)).not.to.throw(); }); }); - it('get() throws errors given invalid keys', function () { - invalidKeys.forEach(function (invalidKey) { - expect(function () { geoFire.get(invalidKey); }).to.throw(); + it('get() throws errors given invalid keys', () => { + invalidKeys.forEach((invalidKey) => { + expect(() => geoFire.get(invalidKey)).to.throw(); }); }); }); - describe('Removing locations:', function () { - it('set() removes existing location given null', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + describe('Removing locations:', () => { + it('set() removes existing location given null', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); geoFire.set({ 'loc1': [0, 0], 'loc2': [2, 3] - }).then(function () { + }).then(() => { cl.x('p1'); return geoFire.get('loc1'); - }).then(function (location) { + }).then((location) => { expect(location).to.deep.equal([0, 0]); cl.x('p2'); return geoFire.set('loc1', null); - }).then(function () { + }).then(() => { cl.x('p3'); return geoFire.get('loc1'); - }).then(function (location) { + }).then((location) => { expect(location).to.equal(null); cl.x('p4'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc2': { '.priority': 's065kk0dc5', 'l': { '0': 2, '1': 3 }, 'g': 's065kk0dc5' } }); @@ -611,30 +611,30 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('set() does nothing given a non-existent location and null', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + it('set() does nothing given a non-existent location and null', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); - geoFire.set('loc1', [0, 0]).then(function () { + geoFire.set('loc1', [0, 0]).then(() => { cl.x('p1'); return geoFire.get('loc1'); - }).then(function (location) { + }).then((location) => { expect(location).to.deep.equal([0, 0]); cl.x('p2'); return geoFire.set('loc2', null); - }).then(function () { + }).then(() => { cl.x('p3'); return geoFire.get('loc2'); - }).then(function (location) { + }).then((location) => { expect(location).to.equal(null); cl.x('p4'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' } }); @@ -643,17 +643,17 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('set() removes existing location given null', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + it('set() removes existing location given null', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); geoFire.set({ 'loc1': [0, 0], 'loc2': [2, 3] - }).then(function () { + }).then(() => { cl.x('p1'); return geoFire.get('loc1'); - }).then(function (location) { + }).then((location) => { expect(location).to.deep.equal([0, 0]); cl.x('p2'); @@ -662,17 +662,17 @@ describe('GeoFire Tests:', function () { 'loc1': null, 'loc3': [-90, -90] }); - }).then(function () { + }).then(() => { cl.x('p3'); return geoFire.get('loc1'); - }).then(function (location) { + }).then((location) => { expect(location).to.equal(null); cl.x('p4'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc2': { '.priority': 's065kk0dc5', 'l': { '0': 2, '1': 3 }, 'g': 's065kk0dc5' }, 'loc3': { '.priority': '1bpbpbpbpb', 'l': { '0': -90, '1': -90 }, 'g': '1bpbpbpbpb' } @@ -682,29 +682,29 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('set() does nothing given a non-existent location and null', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); + it('set() does nothing given a non-existent location and null', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); geoFire.set({ 'loc1': [0, 0], 'loc2': null - }).then(function () { + }).then(() => { cl.x('p1'); return geoFire.get('loc1'); - }).then(function (location) { + }).then((location) => { expect(location).to.deep.equal([0, 0]); cl.x('p2'); return geoFire.get('loc2'); - }).then(function (location) { + }).then((location) => { expect(location).to.equal(null); cl.x('p3'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' } }); @@ -713,33 +713,33 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('remove() removes existing location', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + it('remove() removes existing location', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); geoFire.set({ 'loc:^%*1': [0, 0], 'loc2': [2, 3] - }).then(function () { + }).then(() => { cl.x('p1'); return geoFire.get('loc:^%*1'); - }).then(function (location) { + }).then((location) => { expect(location).to.deep.equal([0, 0]); cl.x('p2'); return geoFire.remove('loc:^%*1'); - }).then(function () { + }).then(() => { cl.x('p3'); return geoFire.get('loc:^%*1'); - }).then(function (location) { + }).then((location) => { expect(location).to.equal(null); cl.x('p4'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc2': { '.priority': 's065kk0dc5', 'l': { '0': 2, '1': 3 }, 'g': 's065kk0dc5' } }); @@ -748,30 +748,30 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('remove() does nothing given a non-existent location', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + it('remove() does nothing given a non-existent location', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); - geoFire.set('loc1', [0, 0]).then(function () { + geoFire.set('loc1', [0, 0]).then(() => { cl.x('p1'); return geoFire.get('loc1'); - }).then(function (location) { + }).then((location) => { expect(location).to.deep.equal([0, 0]); cl.x('p2'); return geoFire.remove('loc2'); - }).then(function () { + }).then(() => { cl.x('p3'); return geoFire.get('loc2'); - }).then(function (location) { + }).then((location) => { expect(location).to.equal(null); cl.x('p4'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc1': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' } }); @@ -780,22 +780,22 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('remove() only removes one key if multiple keys are at the same location', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + it('remove() only removes one key if multiple keys are at the same location', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); geoFire.set({ 'loc1': [0, 0], 'loc2': [2, 3], 'loc3': [0, 0] - }).then(function () { + }).then(() => { cl.x('p1'); return geoFire.remove('loc1'); - }).then(function () { + }).then(() => { cl.x('p2'); return getFirebaseData(); - }).then(function (firebaseData) { + }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ 'loc2': { '.priority': 's065kk0dc5', 'l': { '0': 2, '1': 3 }, 'g': 's065kk0dc5' }, 'loc3': { '.priority': '7zzzzzzzzz', 'l': { '0': 0, '1': 0 }, 'g': '7zzzzzzzzz' } @@ -805,37 +805,37 @@ describe('GeoFire Tests:', function () { }).catch(failTestOnCaughtError); }); - it('remove() does not throw errors given valid keys', function () { - validKeys.forEach(function (validKey) { - expect(function () { geoFire.remove(validKey); }).not.to.throw(); + it('remove() does not throw errors given valid keys', () => { + validKeys.forEach((validKey) => { + expect(() => geoFire.remove(validKey)).not.to.throw(); }); }); - it('remove() throws errors given invalid keys', function () { - invalidKeys.forEach(function (invalidKey) { - expect(function () { geoFire.remove(invalidKey); }).to.throw(); + it('remove() throws errors given invalid keys', () => { + invalidKeys.forEach((invalidKey) => { + expect(() => geoFire.remove(invalidKey)).to.throw(); }); }); }); - describe('query():', function () { - it('query() returns GeoQuery instance', function () { + describe('query():', () => { + it('query() returns GeoQuery instance', () => { geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); expect(geoQueries[0] instanceof GeoQuery).to.be.ok; }); - it('query() does not throw errors given valid query criteria', function () { - validQueryCriterias.forEach(function (validQueryCriteria) { + it('query() does not throw errors given valid query criteria', () => { + validQueryCriterias.forEach((validQueryCriteria) => { if (typeof validQueryCriteria.center !== 'undefined' && typeof validQueryCriteria.radius !== 'undefined') { - expect(function () { geoFire.query(validQueryCriteria); }).not.to.throw(); + expect(() => geoFire.query(validQueryCriteria)).not.to.throw(); } }); }); - it('query() throws errors given invalid query criteria', function () { - invalidQueryCriterias.forEach(function (invalidQueryCriteria) { - expect(function () { geoFire.query(invalidQueryCriteria); }).to.throw(); + it('query() throws errors given invalid query criteria', () => { + invalidQueryCriterias.forEach((invalidQueryCriteria) => { + expect(() => geoFire.query(invalidQueryCriteria)).to.throw(); }); }); }); diff --git a/tests/geoFireUtils.test.ts b/tests/geoFireUtils.test.ts index 2a8b23a0..c80f7300 100644 --- a/tests/geoFireUtils.test.ts +++ b/tests/geoFireUtils.test.ts @@ -116,14 +116,14 @@ describe('geoFireUtils Tests:', () => { it('dist() calculates the distance between locations', () => { expect(GeoFire.distance([90, 180], [90, 180])).to.be.closeTo(0, 0); - expect(GeoFire.distance([-90, -180], [90, 180])).to.be.closeTo(20015, 0); - expect(GeoFire.distance([-90, -180], [-90, 180])).to.be.closeTo(0, 0); - expect(GeoFire.distance([-90, -180], [90, -180])).to.be.closeTo(20015, 0); - expect(GeoFire.distance([37.7853074, -122.4054274], [78.216667, 15.55])).to.be.closeTo(6818, 0); - expect(GeoFire.distance([38.98719, -77.250783], [29.3760648, 47.9818853])).to.be.closeTo(10531, 0); - expect(GeoFire.distance([38.98719, -77.250783], [-54.933333, -67.616667])).to.be.closeTo(10484, 0); - expect(GeoFire.distance([29.3760648, 47.9818853], [-54.933333, -67.616667])).to.be.closeTo(14250, 0); - expect(GeoFire.distance([-54.933333, -67.616667], [-54, -67])).to.be.closeTo(111, 0); + expect(GeoFire.distance([-90, -180], [90, 180])).to.be.closeTo(20015, 1); + expect(GeoFire.distance([-90, -180], [-90, 180])).to.be.closeTo(0, 1); + expect(GeoFire.distance([-90, -180], [90, -180])).to.be.closeTo(20015, 1); + expect(GeoFire.distance([37.7853074, -122.4054274], [78.216667, 15.55])).to.be.closeTo(6818, 1); + expect(GeoFire.distance([38.98719, -77.250783], [29.3760648, 47.9818853])).to.be.closeTo(10531, 1); + expect(GeoFire.distance([38.98719, -77.250783], [-54.933333, -67.616667])).to.be.closeTo(10484, 1); + expect(GeoFire.distance([29.3760648, 47.9818853], [-54.933333, -67.616667])).to.be.closeTo(14250, 1); + expect(GeoFire.distance([-54.933333, -67.616667], [-54, -67])).to.be.closeTo(111, 1); }); it('dist() does not throw errors given valid locations', () => { @@ -145,34 +145,34 @@ describe('geoFireUtils Tests:', () => { describe('Geohashing:', () => { it('encodeGeohash() encodes locations to geohashes given no precision', () => { - expect(encodeGeohash([-90, -180])).to.be('000000000000'.slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([90, 180])).to.be('zzzzzzzzzzzz'.slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([-90, 180])).to.be('pbpbpbpbpbpb'.slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([90, -180])).to.be('bpbpbpbpbpbp'.slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([37.7853074, -122.4054274])).to.be('9q8yywe56gcf'.slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([38.98719, -77.250783])).to.be('dqcjf17sy6cp'.slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([29.3760648, 47.9818853])).to.be('tj4p5gerfzqu'.slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([78.216667, 15.55])).to.be('umghcygjj782'.slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([-54.933333, -67.616667])).to.be('4qpzmren1kwb'.slice(0, g_GEOHASH_PRECISION)); - expect(encodeGeohash([-54, -67])).to.be('4w2kg3s54y7h'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([-90, -180])).to.be.equal('000000000000'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([90, 180])).to.be.equal('zzzzzzzzzzzz'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([-90, 180])).to.be.equal('pbpbpbpbpbpb'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([90, -180])).to.be.equal('bpbpbpbpbpbp'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([37.7853074, -122.4054274])).to.be.equal('9q8yywe56gcf'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([38.98719, -77.250783])).to.be.equal('dqcjf17sy6cp'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([29.3760648, 47.9818853])).to.be.equal('tj4p5gerfzqu'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([78.216667, 15.55])).to.be.equal('umghcygjj782'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([-54.933333, -67.616667])).to.be.equal('4qpzmren1kwb'.slice(0, g_GEOHASH_PRECISION)); + expect(encodeGeohash([-54, -67])).to.be.equal('4w2kg3s54y7h'.slice(0, g_GEOHASH_PRECISION)); }); it('encodeGeohash() encodes locations to geohashes given a custom precision', () => { - expect(encodeGeohash([-90, -180], 6)).to.be('000000'); - expect(encodeGeohash([90, 180], 20)).to.be('zzzzzzzzzzzzzzzzzzzz'); - expect(encodeGeohash([-90, 180], 1)).to.be('p'); - expect(encodeGeohash([90, -180], 5)).to.be('bpbpb'); - expect(encodeGeohash([37.7853074, -122.4054274], 8)).to.be('9q8yywe5'); - expect(encodeGeohash([38.98719, -77.250783], 18)).to.be('dqcjf17sy6cppp8vfn'); - expect(encodeGeohash([29.3760648, 47.9818853], 12)).to.be('tj4p5gerfzqu'); - expect(encodeGeohash([78.216667, 15.55], 1)).to.be('u'); - expect(encodeGeohash([-54.933333, -67.616667], 7)).to.be('4qpzmre'); - expect(encodeGeohash([-54, -67], 9)).to.be('4w2kg3s54'); + expect(encodeGeohash([-90, -180], 6)).to.be.equal('000000'); + expect(encodeGeohash([90, 180], 20)).to.be.equal('zzzzzzzzzzzzzzzzzzzz'); + expect(encodeGeohash([-90, 180], 1)).to.be.equal('p'); + expect(encodeGeohash([90, -180], 5)).to.be.equal('bpbpb'); + expect(encodeGeohash([37.7853074, -122.4054274], 8)).to.be.equal('9q8yywe5'); + expect(encodeGeohash([38.98719, -77.250783], 18)).to.be.equal('dqcjf17sy6cppp8vfn'); + expect(encodeGeohash([29.3760648, 47.9818853], 12)).to.be.equal('tj4p5gerfzqu'); + expect(encodeGeohash([78.216667, 15.55], 1)).to.be.equal('u'); + expect(encodeGeohash([-54.933333, -67.616667], 7)).to.be.equal('4qpzmre'); + expect(encodeGeohash([-54, -67], 9)).to.be.equal('4w2kg3s54'); }); it('encodeGeohash() does not throw errors given valid locations', () => { validLocations.forEach((validLocation, i) => { - expect(() => { encodeGeohash(validLocation); }).not.to.throw(); + expect(() => encodeGeohash(validLocation)).not.to.throw(); }); }); @@ -184,7 +184,7 @@ describe('geoFireUtils Tests:', () => { }); it('encodeGeohash() does not throw errors given valid precision', () => { - var validPrecisions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, undefined]; + const validPrecisions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, undefined]; validPrecisions.forEach((validPrecision, i) => { expect(() => encodeGeohash([0, 0], validPrecision)).not.to.throw(); @@ -192,7 +192,7 @@ describe('geoFireUtils Tests:', () => { }); it('encodeGeohash() throws errors given invalid precision', () => { - var invalidPrecisions = [0, -1, 1.5, 23, '', 'a', true, false, [], {}, [1], { a: 1 }, null]; + const invalidPrecisions = [0, -1, 1.5, 23, '', 'a', true, false, [], {}, [1], { a: 1 }, null]; invalidPrecisions.forEach((invalidPrecision, i) => { // @ts-ignore @@ -258,34 +258,34 @@ describe('geoFireUtils Tests:', () => { describe('Geohash queries:', () => { it('Geohash queries must be of the right size', () => { - expect(geohashQuery('64m9yn96mx', 6)).to.equal(['60', '6h']); - expect(geohashQuery('64m9yn96mx', 1)).to.equal(['0', 'h']); - expect(geohashQuery('64m9yn96mx', 10)).to.equal(['64', '65']); - expect(geohashQuery('6409yn96mx', 11)).to.equal(['640', '64h']); - expect(geohashQuery('64m9yn96mx', 11)).to.equal(['64h', '64~']); - expect(geohashQuery('6', 10)).to.equal(['6', '6~']); - expect(geohashQuery('64z178', 12)).to.equal(['64s', '64~']); - expect(geohashQuery('64z178', 15)).to.equal(['64z', '64~']); + expect(geohashQuery('64m9yn96mx', 6)).to.be.deep.equal(['60', '6h']); + expect(geohashQuery('64m9yn96mx', 1)).to.be.deep.equal(['0', 'h']); + expect(geohashQuery('64m9yn96mx', 10)).to.be.deep.equal(['64', '65']); + expect(geohashQuery('6409yn96mx', 11)).to.be.deep.equal(['640', '64h']); + expect(geohashQuery('64m9yn96mx', 11)).to.be.deep.equal(['64h', '64~']); + expect(geohashQuery('6', 10)).to.be.deep.equal(['6', '6~']); + expect(geohashQuery('64z178', 12)).to.be.deep.equal(['64s', '64~']); + expect(geohashQuery('64z178', 15)).to.be.deep.equal(['64z', '64~']); }); it('Queries from geohashQueries must contain points in circle', () => { function inQuery(queries, hash) { - for (var i = 0; i < queries.length; i++) { + for (let i = 0; i < queries.length; i++) { if (hash >= queries[i][0] && hash < queries[i][1]) { return true; } } return false; } - for (var i = 0; i < 200; i++) { - var centerLat = Math.pow(Math.random(), 5) * 160 - 80; - var centerLong = Math.pow(Math.random(), 5) * 360 - 180; - var radius = Math.random() * Math.random() * 100000; - var degreeRadius = metersToLongitudeDegrees(radius, centerLat); - var queries = geohashQueries([centerLat, centerLong], radius); - for (var j = 0; j < 1000; j++) { - var pointLat = Math.max(-89.9, Math.min(89.9, centerLat + Math.random() * degreeRadius)); - var pointLong = wrapLongitude(centerLong + Math.random() * degreeRadius); + for (let i = 0; i < 200; i++) { + const centerLat = Math.pow(Math.random(), 5) * 160 - 80; + const centerLong = Math.pow(Math.random(), 5) * 360 - 180; + const radius = Math.random() * Math.random() * 100000; + const degreeRadius = metersToLongitudeDegrees(radius, centerLat); + const queries = geohashQueries([centerLat, centerLong], radius); + for (let j = 0; j < 1000; j++) { + const pointLat = Math.max(-89.9, Math.min(89.9, centerLat + Math.random() * degreeRadius)); + const pointLong = wrapLongitude(centerLong + Math.random() * degreeRadius); if (GeoFire.distance([centerLat, centerLong], [pointLat, pointLong]) < radius / 1000) { expect(inQuery(queries, encodeGeohash([pointLat, pointLong]))).to.be.true; } diff --git a/tests/geoQuery.test.ts b/tests/geoQuery.test.ts index 44ce7c2d..dfa4eeb9 100644 --- a/tests/geoQuery.test.ts +++ b/tests/geoQuery.test.ts @@ -25,25 +25,25 @@ describe('GeoQuery Tests:', () => { }); it('Constructor throws error on invalid query criteria', () => { - expect(() => { geoFire.query({}) }).to.throw(); - expect(() => { geoFire.query({ random: 100 }) }).to.throw(); - expect(() => { geoFire.query({ center: [1, 2] }) }).to.throw(); - expect(() => { geoFire.query({ radius: 1000 }) }).to.throw(); - expect(() => { geoFire.query({ center: [91, 2], radius: 1000 }) }).to.throw(); - expect(() => { geoFire.query({ center: [1, -181], radius: 1000 }) }).to.throw(); - expect(() => { geoFire.query({ center: ['text', 2], radius: 1000 }) }).to.throw(); - expect(() => { geoFire.query({ center: [1, [1, 2]], radius: 1000 }) }).to.throw(); - expect(() => { geoFire.query({ center: 1000, radius: 1000 }) }).to.throw(); - expect(() => { geoFire.query({ center: null, radius: 1000 }) }).to.throw(); - expect(() => { geoFire.query({ center: undefined, radius: 1000 }) }).to.throw(); - expect(() => { geoFire.query({ center: [null, 2], radius: 1000 }) }).to.throw(); - expect(() => { geoFire.query({ center: [1, undefined], radius: 1000 }) }).to.throw(); - expect(() => { geoFire.query({ center: [1, 2], radius: -10 }) }).to.throw(); - expect(() => { geoFire.query({ center: [1, 2], radius: 'text' }) }).to.throw(); - expect(() => { geoFire.query({ center: [1, 2], radius: [1, 2] }) }).to.throw(); - expect(() => { geoFire.query({ center: [1, 2], radius: null }) }).to.throw(); - expect(() => { geoFire.query({ center: [1, 2], radius: undefined }) }).to.throw(); - expect(() => { geoFire.query({ center: [1, 2], radius: 1000, other: 'throw' }) }).to.throw(); + expect(() => geoFire.query({})).to.throw(); + expect(() => geoFire.query({ random: 100 })).to.throw(); + expect(() => geoFire.query({ center: [1, 2] })).to.throw(); + expect(() => geoFire.query({ radius: 1000 })).to.throw(); + expect(() => geoFire.query({ center: [91, 2], radius: 1000 })).to.throw(); + expect(() => geoFire.query({ center: [1, -181], radius: 1000 })).to.throw(); + expect(() => geoFire.query({ center: ['text', 2], radius: 1000 })).to.throw(); + expect(() => geoFire.query({ center: [1, [1, 2]], radius: 1000 })).to.throw(); + expect(() => geoFire.query({ center: 1000, radius: 1000 })).to.throw(); + expect(() => geoFire.query({ center: null, radius: 1000 })).to.throw(); + expect(() => geoFire.query({ center: undefined, radius: 1000 })).to.throw(); + expect(() => geoFire.query({ center: [null, 2], radius: 1000 })).to.throw(); + expect(() => geoFire.query({ center: [1, undefined], radius: 1000 })).to.throw(); + expect(() => geoFire.query({ center: [1, 2], radius: -10 })).to.throw(); + expect(() => geoFire.query({ center: [1, 2], radius: 'text' })).to.throw(); + expect(() => geoFire.query({ center: [1, 2], radius: [1, 2] })).to.throw(); + expect(() => geoFire.query({ center: [1, 2], radius: null })).to.throw(); + expect(() => geoFire.query({ center: [1, 2], radius: undefined })).to.throw(); + expect(() => geoFire.query({ center: [1, 2], radius: 1000, other: 'throw' })).to.throw(); }); }); @@ -84,11 +84,11 @@ describe('GeoQuery Tests:', () => { expect(geoQueries[0].radius()).to.equal(100); }); - it('updateCriteria() fires \'key_entered\' callback for locations which now belong to the GeoQuery', function (done) { - var cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); + it('updateCriteria() fires \'key_entered\' callback for locations which now belong to the GeoQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); geoQueries.push(geoFire.query({ center: [90, 90], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -109,11 +109,11 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('updateCriteria() fires \'key_entered\' callback for locations with complex keys which now belong to the GeoQuery', function (done) { - var cl = new Checklist(['p1', 'p2', 'loc:^:*1 entered', 'loc-+-+-4 entered'], expect, done); + it('updateCriteria() fires \'key_entered\' callback for locations with complex keys which now belong to the GeoQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'loc:^:*1 entered', 'loc-+-+-4 entered'], expect, done); geoQueries.push(geoFire.query({ center: [90, 90], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -134,11 +134,11 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('updateCriteria() fires \'key_exited\' callback for locations which no longer belong to the GeoQuery', function (done) { - var cl = new Checklist(['p1', 'p2', 'loc1 exited', 'loc4 exited'], expect, done); + it('updateCriteria() fires \'key_exited\' callback for locations which no longer belong to the GeoQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'loc1 exited', 'loc4 exited'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -159,14 +159,14 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('updateCriteria() does not cause event callbacks to fire on the previous criteria', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc1 exited', 'loc4 exited', 'loc4 entered', 'loc5 entered'], expect, done); + it('updateCriteria() does not cause event callbacks to fire on the previous criteria', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc1 exited', 'loc4 exited', 'loc4 entered', 'loc5 entered'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -198,17 +198,17 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('updateCriteria() does not cause \'key_moved\' callbacks to fire for keys in both the previous and updated queries', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc4 exited', 'loc2 entered'], expect, done); + it('updateCriteria() does not cause \'key_moved\' callbacks to fire for keys in both the previous and updated queries', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc4 exited', 'loc2 entered'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -240,17 +240,17 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('updateCriteria() does not cause \'key_exited\' callbacks to fire twice for keys in the previous query but not in the updated query and which were moved after the query was updated', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'loc1 entered', 'loc4 entered', 'loc1 exited', 'loc4 exited', 'loc4 entered', 'loc5 entered', 'loc5 moved'], expect, done); + it('updateCriteria() does not cause \'key_exited\' callbacks to fire twice for keys in the previous query but not in the updated query and which were moved after the query was updated', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'loc1 entered', 'loc4 entered', 'loc1 exited', 'loc4 exited', 'loc4 entered', 'loc5 entered', 'loc5 moved'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -297,16 +297,16 @@ describe('GeoQuery Tests:', () => { it('updateCriteria() does not throw errors given valid query criteria', () => { geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - validQueryCriterias.forEach(function (validQueryCriteria) { - expect(() => { geoQueries[0].updateCriteria(validQueryCriteria); }).not.to.throw(); + validQueryCriterias.forEach((validQueryCriteria) => { + expect(() => geoQueries[0].updateCriteria(validQueryCriteria)).not.to.throw(); }); }); it('updateCriteria() throws errors given invalid query criteria', () => { geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - invalidQueryCriterias.forEach(function (invalidQueryCriteria) { - expect(() => { geoQueries[0].updateCriteria(invalidQueryCriteria); }).to.throw(); + invalidQueryCriterias.forEach((invalidQueryCriteria) => { + expect(() => geoQueries[0].updateCriteria(invalidQueryCriteria)).to.throw(); }); }); }); @@ -315,7 +315,7 @@ describe('GeoQuery Tests:', () => { it('on() throws error given invalid event type', () => { geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - var setInvalidEventType = () => { + const setInvalidEventType = () => { geoQueries[0].on('invalid_event', () => { }); } @@ -325,7 +325,7 @@ describe('GeoQuery Tests:', () => { it('on() throws error given invalid callback', () => { geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - var setInvalidCallback = () => { + const setInvalidCallback = () => { geoQueries[0].on('key_entered', 'non-function'); } @@ -334,8 +334,8 @@ describe('GeoQuery Tests:', () => { }); describe('\'ready\' event:', () => { - it('\'ready\' event fires after all \'key_entered\' events have fired', function (done) { - var cl = new Checklist(['p1', 'loc1 entered', 'loc2 entered', 'loc5 entered', 'loc6 entered', 'loc7 entered', 'loc10 entered', 'ready fired'], expect, done); + it('\'ready\' event fires after all \'key_entered\' events have fired', (done) => { + const cl = new Checklist(['p1', 'loc1 entered', 'loc2 entered', 'loc5 entered', 'loc6 entered', 'loc7 entered', 'loc10 entered', 'ready fired'], expect, done); geoFire.set({ 'loc1': [0, 0], @@ -353,7 +353,7 @@ describe('GeoQuery Tests:', () => { geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -364,8 +364,8 @@ describe('GeoQuery Tests:', () => { }); }); - it('\'ready\' event fires immediately if the callback is added after the query is already ready', function (done) { - var cl = new Checklist(['p1', 'loc1 entered', 'loc2 entered', 'loc5 entered', 'loc6 entered', 'loc7 entered', 'loc10 entered', 'ready1 fired', 'ready2 fired'], expect, done); + it('\'ready\' event fires immediately if the callback is added after the query is already ready', (done) => { + const cl = new Checklist(['p1', 'loc1 entered', 'loc2 entered', 'loc5 entered', 'loc6 entered', 'loc7 entered', 'loc10 entered', 'ready1 fired', 'ready2 fired'], expect, done); geoFire.set({ 'loc1': [0, 0], @@ -383,7 +383,7 @@ describe('GeoQuery Tests:', () => { geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -398,10 +398,10 @@ describe('GeoQuery Tests:', () => { }); }); - it('\'ready\' event fires after increasing the query radius, even if no new geohashes were queried', function (done) { - var cl = new Checklist(['ready1 fired', 'ready2 fired'], expect, done); + it('\'ready\' event fires after increasing the query radius, even if no new geohashes were queried', (done) => { + const cl = new Checklist(['ready1 fired', 'ready2 fired'], expect, done); geoQueries.push(geoFire.query({ center: [37.7851382, -122.405893], radius: 6 })); - var onReadyCallbackRegistration1 = geoQueries[0].on('ready', () => { + const onReadyCallbackRegistration1 = geoQueries[0].on('ready', () => { cl.x('ready1 fired'); onReadyCallbackRegistration1.cancel(); geoQueries[0].updateCriteria({ @@ -413,8 +413,8 @@ describe('GeoQuery Tests:', () => { }); }); - it('updateCriteria() fires the \'ready\' event after all \'key_entered\' events have fired', function (done) { - var cl = new Checklist(['p1', 'loc1 entered', 'loc2 entered', 'loc5 entered', 'loc3 entered', 'loc1 exited', 'loc2 exited', 'loc5 exited', 'ready1 fired', 'ready2 fired'], expect, done); + it('updateCriteria() fires the \'ready\' event after all \'key_entered\' events have fired', (done) => { + const cl = new Checklist(['p1', 'loc1 entered', 'loc2 entered', 'loc5 entered', 'loc3 entered', 'loc1 exited', 'loc2 exited', 'loc5 exited', 'ready1 fired', 'ready2 fired'], expect, done); geoFire.set({ 'loc1': [0, 0], @@ -427,15 +427,15 @@ describe('GeoQuery Tests:', () => { geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - var onReadyCallbackRegistration1 = geoQueries[0].on('ready', () => { + const onReadyCallbackRegistration1 = geoQueries[0].on('ready', () => { expect(cl.length()).to.be.equal(6); cl.x('ready1 fired'); @@ -455,12 +455,12 @@ describe('GeoQuery Tests:', () => { }); describe('\'key_moved\' event:', () => { - it('\'key_moved\' callback does not fire for brand new locations within or outside of the GeoQuery', function (done) { - var cl = new Checklist(['p1', 'p2'], expect, done); + it('\'key_moved\' callback does not fire for brand new locations within or outside of the GeoQuery', (done) => { + const cl = new Checklist(['p1', 'p2'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -477,12 +477,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback does not fire for locations outside of the GeoQuery which are moved somewhere else outside of the GeoQuery', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + it('\'key_moved\' callback does not fire for locations outside of the GeoQuery which are moved somewhere else outside of the GeoQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -506,12 +506,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback does not fire for locations outside of the GeoQuery which are moved within the GeoQuery', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + it('\'key_moved\' callback does not fire for locations outside of the GeoQuery which are moved within the GeoQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -535,12 +535,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback does not fire for locations within the GeoQuery which are moved somewhere outside of the GeoQuery', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + it('\'key_moved\' callback does not fire for locations within the GeoQuery which are moved somewhere outside of the GeoQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -564,12 +564,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback does not fires for a location within the GeoQuery which is set to the same location', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc3 moved'], expect, done); + it('\'key_moved\' callback does not fires for a location within the GeoQuery which is set to the same location', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc3 moved'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -594,12 +594,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback fires for locations within the GeoQuery which are moved somewhere else within the GeoQuery', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); + it('\'key_moved\' callback fires for locations within the GeoQuery which are moved somewhere else within the GeoQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -623,12 +623,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback gets passed correct location parameter', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved to 2,2', 'loc3 moved to -1,-1'], expect, done); + it('\'key_moved\' callback gets passed correct location parameter', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved to 2,2', 'loc3 moved to -1,-1'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved to ' + location); }); @@ -652,12 +652,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback gets passed correct distance parameter', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved (111.19 km from center)', 'loc3 moved (400.90 km from center)'], expect, done); + it('\'key_moved\' callback gets passed correct distance parameter', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved (111.19 km from center)', 'loc3 moved (400.90 km from center)'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved (' + distance.toFixed(2) + ' km from center)'); }); @@ -681,12 +681,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback properly fires when multiple keys are at the same location within the GeoQuery and only one of them moves somewhere else within the GeoQuery', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); + it('\'key_moved\' callback properly fires when multiple keys are at the same location within the GeoQuery and only one of them moves somewhere else within the GeoQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -710,12 +710,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback properly fires when a location within the GeoQuery moves somehwere else within the GeoQuery that is already occupied by another key', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); + it('\'key_moved\' callback properly fires when a location within the GeoQuery moves somehwere else within the GeoQuery that is already occupied by another key', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -739,15 +739,15 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('multiple \'key_moved\' callbacks fire for locations within the GeoQuery which are moved somewhere else within the GeoQuery', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved1', 'loc3 moved1', 'loc1 moved2', 'loc3 moved2'], expect, done); + it('multiple \'key_moved\' callbacks fire for locations within the GeoQuery which are moved somewhere else within the GeoQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved1', 'loc3 moved1', 'loc1 moved2', 'loc3 moved2'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved1'); }); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved2'); }); @@ -773,8 +773,8 @@ describe('GeoQuery Tests:', () => { }); describe('\'key_entered\' event:', () => { - it('\'key_entered\' callback fires when a location enters the GeoQuery before onKeyEntered() was called', function (done) { - var cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); + it('\'key_entered\' callback fires when a location enters the GeoQuery before onKeyEntered() was called', (done) => { + const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -787,7 +787,7 @@ describe('GeoQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -797,12 +797,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_entered\' callback fires when a location enters the GeoQuery after onKeyEntered() was called', function (done) { - var cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); + it('\'key_entered\' callback fires when a location enters the GeoQuery after onKeyEntered() was called', (done) => { + const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -821,12 +821,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_entered\' callback gets passed correct location parameter', function (done) { - var cl = new Checklist(['p1', 'p2', 'loc1 entered at 2,3', 'loc4 entered at 5,5'], expect, done); + it('\'key_entered\' callback gets passed correct location parameter', (done) => { + const cl = new Checklist(['p1', 'p2', 'loc1 entered at 2,3', 'loc4 entered at 5,5'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered at ' + location); }); @@ -845,12 +845,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_entered\' callback gets passed correct distance parameter', function (done) { - var cl = new Checklist(['p1', 'p2', 'loc1 entered (157.23 km from center)', 'loc4 entered (555.66 km from center)'], expect, done); + it('\'key_entered\' callback gets passed correct distance parameter', (done) => { + const cl = new Checklist(['p1', 'p2', 'loc1 entered (157.23 km from center)', 'loc4 entered (555.66 km from center)'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered (' + distance.toFixed(2) + ' km from center)'); }); @@ -869,12 +869,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_entered\' callback properly fires when multiple keys are at the same location outside the GeoQuery and only one of them moves within the GeoQuery', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 entered'], expect, done); + it('\'key_entered\' callback properly fires when multiple keys are at the same location outside the GeoQuery and only one of them moves within the GeoQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 entered'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -895,12 +895,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_entered\' callback properly fires when a location outside the GeoQuery moves somewhere within the GeoQuery that is already occupied by another key', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 entered', 'loc3 entered'], expect, done); + it('\'key_entered\' callback properly fires when a location outside the GeoQuery moves somewhere within the GeoQuery that is already occupied by another key', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 entered', 'loc3 entered'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -921,15 +921,15 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('multiple \'key_entered\' callbacks fire when a location enters the GeoQuery', function (done) { - var cl = new Checklist(['p1', 'p2', 'loc1 entered1', 'loc4 entered1', 'loc1 entered2', 'loc4 entered2'], expect, done); + it('multiple \'key_entered\' callbacks fire when a location enters the GeoQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'loc1 entered1', 'loc4 entered1', 'loc1 entered2', 'loc4 entered2'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered1'); }); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered2'); }); @@ -950,12 +950,12 @@ describe('GeoQuery Tests:', () => { }); describe('\'key_exited\' event:', () => { - it('\'key_exited\' callback fires when a location leaves the GeoQuery', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited', 'loc4 exited'], expect, done); + it('\'key_exited\' callback fires when a location leaves the GeoQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited', 'loc4 exited'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -981,12 +981,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_exited\' callback gets passed correct location parameter', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited to 25,90', 'loc4 exited to 25,5'], expect, done); + it('\'key_exited\' callback gets passed correct location parameter', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited to 25,90', 'loc4 exited to 25,5'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited to ' + location); }); @@ -1013,12 +1013,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_exited\' callback gets passed correct distance parameter', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited (9759.01 km from center)', 'loc4 exited (2688.06 km from center)'], expect, done); + it('\'key_exited\' callback gets passed correct distance parameter', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited (9759.01 km from center)', 'loc4 exited (2688.06 km from center)'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited (' + distance.toFixed(2) + ' km from center)'); }); @@ -1045,12 +1045,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_exited\' callback gets passed null for location and distance parameters if the key is entirely removed from GeoFire', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); + it('\'key_exited\' callback gets passed null for location and distance parameters if the key is entirely removed from GeoFire', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { expect(location).to.be.equal(null); expect(distance).to.be.equal(null); cl.x(key + ' exited'); @@ -1069,12 +1069,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_exited\' callback fires when a location within the GeoQuery is entirely removed from GeoFire', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); + it('\'key_exited\' callback fires when a location within the GeoQuery is entirely removed from GeoFire', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -1094,11 +1094,11 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_exited\' callback properly fires when multiple keys are at the same location inside the GeoQuery and only one of them moves outside the GeoQuery', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); + it('\'key_exited\' callback properly fires when multiple keys are at the same location inside the GeoQuery and only one of them moves outside the GeoQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -1119,12 +1119,12 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_exited\' callback properly fires when a location inside the GeoQuery moves somewhere outside the GeoQuery that is already occupied by another key', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); + it('\'key_exited\' callback properly fires when a location inside the GeoQuery moves somewhere outside the GeoQuery that is already occupied by another key', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -1145,15 +1145,15 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('multiple \'key_exited\' callbacks fire when a location leaves the GeoQuery', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited1', 'loc4 exited1', 'loc1 exited2', 'loc4 exited2'], expect, done); + it('multiple \'key_exited\' callbacks fire when a location leaves the GeoQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited1', 'loc4 exited1', 'loc1 exited2', 'loc4 exited2'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited1'); }); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited2'); }); @@ -1181,18 +1181,18 @@ describe('GeoQuery Tests:', () => { }); describe('\'key_*\' events combined:', () => { - it('\'key_*\' event callbacks fire when used all at the same time', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc1 moved', 'loc4 exited', 'loc1 exited', 'loc5 entered'], expect, done); + it('\'key_*\' event callbacks fire when used all at the same time', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc1 moved', 'loc4 exited', 'loc1 exited', 'loc5 entered'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -1226,18 +1226,18 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('location moving between geohash queries triggers a key_moved', function (done) { - var cl = new Checklist(['loc1 entered', 'loc2 entered', 'p1', 'loc1 moved', 'loc2 moved', 'p2'], expect, done); + it('location moving between geohash queries triggers a key_moved', (done) => { + const cl = new Checklist(['loc1 entered', 'loc2 entered', 'p1', 'loc1 moved', 'loc2 moved', 'p2'], expect, done); geoQueries.push(geoFire.query({ center: [0, 0], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -1260,18 +1260,18 @@ describe('GeoQuery Tests:', () => { }); describe('Cancelling GeoQuery:', () => { - it('cancel() prevents GeoQuery from firing any more \'key_*\' event callbacks', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 entered', 'loc4 entered', 'loc1 moved', 'loc4 exited'], expect, done); + it('cancel() prevents GeoQuery from firing any more \'key_*\' event callbacks', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 entered', 'loc4 entered', 'loc1 moved', 'loc4 exited'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -1299,13 +1299,13 @@ describe('GeoQuery Tests:', () => { return wait(1000); }).then(() => { - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -1323,29 +1323,29 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('Calling cancel() on one GeoQuery does not cancel other GeoQueries', function (done) { - var cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 entered1', 'loc1 entered2', 'loc4 entered1', 'loc4 entered2', 'loc1 moved1', 'loc1 moved2', 'loc4 exited1', 'loc4 exited2', 'loc1 exited2', 'loc5 entered2'], expect, done); + it('Calling cancel() on one GeoQuery does not cancel other GeoQueries', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 entered1', 'loc1 entered2', 'loc4 entered1', 'loc4 entered2', 'loc1 moved1', 'loc1 moved2', 'loc4 exited1', 'loc4 exited2', 'loc1 exited2', 'loc5 entered2'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', function (key, location, distance) { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered1'); }); - geoQueries[0].on('key_exited', function (key, location, distance) { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited1'); }); - geoQueries[0].on('key_moved', function (key, location, distance) { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved1'); }); - geoQueries[1].on('key_entered', function (key, location, distance) { + geoQueries[1].on('key_entered', (key, location, distance) => { cl.x(key + ' entered2'); }); - geoQueries[1].on('key_exited', function (key, location, distance) { + geoQueries[1].on('key_exited', (key, location, distance) => { cl.x(key + ' exited2'); }); - geoQueries[1].on('key_moved', function (key, location, distance) { + geoQueries[1].on('key_moved', (key, location, distance) => { cl.x(key + ' moved2'); }); @@ -1385,8 +1385,8 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('Calling cancel() in the middle of firing \'key_entered\' events is allowed', function (done) { - var cl = new Checklist(['p1', 'key entered', 'cancel query'], expect, done); + it('Calling cancel() in the middle of firing \'key_entered\' events is allowed', (done) => { + const cl = new Checklist(['p1', 'key entered', 'cancel query'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -1397,8 +1397,8 @@ describe('GeoQuery Tests:', () => { }).then(() => { cl.x('p1'); - var numKeyEnteredEventsFired = 0; - geoQueries[0].on('key_entered', function (key, location, distance) { + let numKeyEnteredEventsFired = 0; + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x('key entered'); numKeyEnteredEventsFired++; if (numKeyEnteredEventsFired === 1) { From 6eb4280c867cc6b5a7301e3253b576291c51bc58 Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Thu, 8 Mar 2018 21:46:51 -0500 Subject: [PATCH 03/23] Fix events for GeoQuery class --- src/firebase/geoQuery.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/firebase/geoQuery.ts b/src/firebase/geoQuery.ts index 70b7f56e..cf0526fb 100644 --- a/src/firebase/geoQuery.ts +++ b/src/firebase/geoQuery.ts @@ -414,9 +414,9 @@ export class GeoQuery { const firebaseQuery: firebase.database.Query = this._firebaseRef.orderByChild('g').startAt(query[0]).endAt(query[1]); // For every new matching geohash, determine if we should fire the 'key_entered' event - const childAddedCallback = firebaseQuery.on('child_added', this._childAddedCallback); - const childRemovedCallback = firebaseQuery.on('child_removed', this._childRemovedCallback); - const childChangedCallback = firebaseQuery.on('child_changed', this._childChangedCallback); + const childAddedCallback = firebaseQuery.on('child_added', (a) => this._childAddedCallback(a)); + const childRemovedCallback = firebaseQuery.on('child_removed', (a) => this._childRemovedCallback(a)); + const childChangedCallback = firebaseQuery.on('child_changed', (a) => this._childChangedCallback(a)); // Once the current geohash to query is processed, see if it is the last one to be processed // and, if so, mark the value event as fired. @@ -496,7 +496,7 @@ export class GeoQuery { * @param key The key of the geofire location. * @param location The location as [latitude, longitude] pair. */ - private _updateLocation(key: string, location: number[]): void { + private _updateLocation(key: string, location?: number[]): void { validateLocation(location); // Get the key and location let distanceFromCenter: number, isInQuery; From e8e2ff52a1a0b72c9b7b131860f85dc31ddd4458 Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Thu, 8 Mar 2018 21:54:20 -0500 Subject: [PATCH 04/23] Fix to kill mocha at end of test --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3f8c5e3e..aa88592b 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "scripts": { "browserify": "browserify dist/index.js -o dist/geofire.js", "build": "tsc && npm run browserify && npm run uglify", - "test": "mocha --reporter spec -r ts-node/register -r jsdom-global/register --timeout 5000 'tests/**/*.test.ts'", + "test": "mocha --reporter spec -r ts-node/register -r jsdom-global/register --timeout 5000 --exit 'tests/**/*.test.ts'", "travis": "npm run test && npm run build", "uglify": "uglifyjs dist/geofire.js -c -m -o dist/geofire.min.js" } From 51e76bdaeef3bad607f134498d8a92301efc436e Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Fri, 9 Mar 2018 01:43:53 -0500 Subject: [PATCH 05/23] feat(firestore): early implementation of geofirestore --- package.json | 6 +- src/{firebase/geoFire.ts => geoFire/index.ts} | 28 +- .../geoQuery.ts => geoFire/query.ts} | 16 +- src/geoFirestore/index.ts | 159 ++++++ src/geoFirestore/query.ts | 532 ++++++++++++++++++ src/index.ts | 16 +- src/interfaces/geoFirestoreQueryState.ts | 5 + src/interfaces/index.ts | 1 + .../callbackRegistration.ts} | 0 .../geoFireUtils.ts => tools/utils.ts} | 18 +- tests/common.ts | 6 +- .../index.test.ts} | 10 +- .../query.test.ts} | 54 +- .../callbackRegistration.test.ts} | 4 +- .../utils.test.ts} | 6 +- 15 files changed, 787 insertions(+), 74 deletions(-) rename src/{firebase/geoFire.ts => geoFire/index.ts} (84%) rename src/{firebase/geoQuery.ts => geoFire/query.ts} (97%) create mode 100644 src/geoFirestore/index.ts create mode 100644 src/geoFirestore/query.ts create mode 100644 src/interfaces/geoFirestoreQueryState.ts rename src/{firebase/geoCallbackRegistration.ts => tools/callbackRegistration.ts} (100%) rename src/{firebase/geoFireUtils.ts => tools/utils.ts} (96%) rename tests/{geoFire.test.ts => geoFire/index.test.ts} (99%) rename tests/{geoQuery.test.ts => geoFire/query.test.ts} (95%) rename tests/{geoCallbackRegistration.test.ts => tools/callbackRegistration.test.ts} (98%) rename tests/{geoFireUtils.test.ts => tools/utils.test.ts} (99%) diff --git a/package.json b/package.json index aa88592b..b5b59ded 100644 --- a/package.json +++ b/package.json @@ -45,10 +45,10 @@ "uglify-js": "^3.3.13" }, "scripts": { - "browserify": "browserify dist/index.js -o dist/geofire.js", - "build": "tsc && npm run browserify && npm run uglify", + "browserify": "browserify dist/geoFire/index.js -o dist/browser/geofire.js && browserify dist/geoFirestore/index.js -o dist/browser/geofirestore.js", + "build": "rm -rf ./dist && tsc && npm run browserify && npm run uglify", "test": "mocha --reporter spec -r ts-node/register -r jsdom-global/register --timeout 5000 --exit 'tests/**/*.test.ts'", "travis": "npm run test && npm run build", - "uglify": "uglifyjs dist/geofire.js -c -m -o dist/geofire.min.js" + "uglify": "uglifyjs dist/browser/geofire.js -c -m -o dist/browser/geofire.min.js && uglifyjs dist/browser/geofirestore.js -c -m -o dist/browser/geofirestore.min.js" } } diff --git a/src/firebase/geoFire.ts b/src/geoFire/index.ts similarity index 84% rename from src/firebase/geoFire.ts rename to src/geoFire/index.ts index 4abb27bf..f1794e7e 100644 --- a/src/firebase/geoFire.ts +++ b/src/geoFire/index.ts @@ -1,7 +1,19 @@ +/*! + * GeoFire is an open-source library that allows you to store and query a set + * of keys based on their geographic location. At its heart, GeoFire simply + * stores locations with string keys. Its main benefit, however, is the + * possibility of retrieving only those keys within a given geographic area - + * all in realtime. + * + * GeoFire 0.0.0 + * https://github.com/firebase/geofire-js/ + * License: MIT + */ + import * as firebase from 'firebase'; -import { GeoQuery } from './geoQuery'; -import { decodeGeoFireObject, degreesToRadians, encodeGeoFireObject, encodeGeohash, validateLocation, validateKey } from './geoFireUtils'; +import { GeoFireQuery } from './query'; +import { decodeGeoFireObject, degreesToRadians, encodeGeoFireObject, encodeGeohash, validateLocation, validateKey } from '../tools/utils'; import { QueryCriteria } from '../interfaces'; @@ -89,7 +101,7 @@ export class GeoFire { const newData = {}; - Object.keys(locations).forEach(function (key) { + Object.keys(locations).forEach((key) => { validateKey(key); const location: number[] = locations[key]; @@ -108,13 +120,13 @@ export class GeoFire { }; /** - * Returns a new GeoQuery instance with the provided queryCriteria. + * Returns a new GeoFireQuery instance with the provided queryCriteria. * - * @param queryCriteria The criteria which specifies the GeoQuery's center and radius. - * @return A new GeoQuery object. + * @param queryCriteria The criteria which specifies the GeoFireQuery's center and radius. + * @return A new GeoFireQuery object. */ - public query(queryCriteria: QueryCriteria): GeoQuery { - return new GeoQuery(this._firebaseRef, queryCriteria); + public query(queryCriteria: QueryCriteria): GeoFireQuery { + return new GeoFireQuery(this._firebaseRef, queryCriteria); }; /********************/ diff --git a/src/firebase/geoQuery.ts b/src/geoFire/query.ts similarity index 97% rename from src/firebase/geoQuery.ts rename to src/geoFire/query.ts index cf0526fb..1696580a 100644 --- a/src/firebase/geoQuery.ts +++ b/src/geoFire/query.ts @@ -1,15 +1,15 @@ import * as firebase from 'firebase'; -import { GeoFire } from './geoFire'; -import { GeoCallbackRegistration } from './geoCallbackRegistration'; -import { decodeGeoFireObject, encodeGeohash, getKey, geohashQueries, validateCriteria, validateLocation } from './geoFireUtils'; +import { GeoFire } from './'; +import { GeoCallbackRegistration } from '../tools/callbackRegistration'; +import { decodeGeoFireObject, encodeGeohash, geoFireGetKey, geohashQueries, validateCriteria, validateLocation } from '../tools/utils'; import { QueryCriteria } from '../interfaces'; /** - * Creates a GeoQuery instance. + * Creates a GeoFireQuery instance. */ -export class GeoQuery { +export class GeoFireQuery { // Event callbacks private _callbacks: any = { ready: [], key_entered: [], key_exited: [], key_moved: [] }; // Variable to track when the query is cancelled @@ -235,7 +235,7 @@ export class GeoQuery { * @param locationDataSnapshot A snapshot of the data stored for this location. */ private _childAddedCallback(locationDataSnapshot: firebase.database.DataSnapshot): void { - this._updateLocation(getKey(locationDataSnapshot), decodeGeoFireObject(locationDataSnapshot.val())); + this._updateLocation(geoFireGetKey(locationDataSnapshot), decodeGeoFireObject(locationDataSnapshot.val())); } /** @@ -244,7 +244,7 @@ export class GeoQuery { * @param locationDataSnapshot A snapshot of the data stored for this location. */ private _childChangedCallback(locationDataSnapshot: firebase.database.DataSnapshot): void { - this._updateLocation(getKey(locationDataSnapshot), decodeGeoFireObject(locationDataSnapshot.val())); + this._updateLocation(geoFireGetKey(locationDataSnapshot), decodeGeoFireObject(locationDataSnapshot.val())); } /** @@ -253,7 +253,7 @@ export class GeoQuery { * @param locationDataSnapshot A snapshot of the data stored for this location. */ private _childRemovedCallback(locationDataSnapshot: firebase.database.DataSnapshot): void { - const key: string = getKey(locationDataSnapshot); + const key: string = geoFireGetKey(locationDataSnapshot); if (this._locationsTracked.hasOwnProperty(key)) { this._firebaseRef.child(key).once('value', (snapshot: firebase.database.DataSnapshot) => { const location: number[] = (snapshot.val() === null) ? null : decodeGeoFireObject(snapshot.val()); diff --git a/src/geoFirestore/index.ts b/src/geoFirestore/index.ts new file mode 100644 index 00000000..1301f9e3 --- /dev/null +++ b/src/geoFirestore/index.ts @@ -0,0 +1,159 @@ +/*! + * GeoFire is an open-source library that allows you to store and query a set + * of keys based on their geographic location. At its heart, GeoFire simply + * stores locations with string keys. Its main benefit, however, is the + * possibility of retrieving only those keys within a given geographic area - + * all in realtime. + * + * GeoFire 0.0.0 + * https://github.com/firebase/geofire-js/ + * License: MIT + */ + +import * as firebase from 'firebase'; + +import { GeoFirestoreQuery } from './query'; +import { decodeGeoFireObject, degreesToRadians, encodeGeoFireObject, encodeGeohash, validateLocation, validateKey } from '../tools/utils'; + +import { QueryCriteria, GeoFireObj } from '../interfaces'; + +/** + * Creates a GeoFirestore instance. + */ +export class GeoFirestore { + /** + * @param _collectionRef A Firestore Collection reference where the GeoFirestore data will be stored. + */ + constructor(private _collectionRef: firebase.firestore.CollectionReference) { + if (Object.prototype.toString.call(this._collectionRef) !== '[object Object]') { + throw new Error('collectionRef must be an instance of a Firestore Collection'); + } + } + + /********************/ + /* PUBLIC METHODS */ + /********************/ + /** + * Returns a promise fulfilled with the location corresponding to the provided key. + * + * If the provided key does not exist, the returned promise is fulfilled with null. + * + * @param key The key of the location to retrieve. + * @returns A promise that is fulfilled with the location of the given key. + */ + public get(key: string): Promise { + validateKey(key); + return this._collectionRef.doc(key).get().then((documentSnapshot: firebase.firestore.DocumentSnapshot) => { + const snapshotVal = documentSnapshot.data(); + if (snapshotVal === null) { + return null; + } else { + return decodeGeoFireObject(snapshotVal); + } + }); + }; + + /** + * Returns the Firestore Collection used to create this GeoFirestore instance. + * + * @returns The Firestore Collection used to create this GeoFirestore instance. + */ + public ref(): firebase.firestore.CollectionReference { + return this._collectionRef; + }; + + /** + * Removes the provided key from this GeoFirestore. Returns an empty promise fulfilled when the key has been removed. + * + * If the provided key is not in this GeoFirestore, the promise will still successfully resolve. + * + * @param key The key of the location to remove. + * @returns A promise that is fulfilled after the inputted key is removed. + */ + public remove(key: string): Promise { + return this.set(key, null); + }; + + /** + * Adds the provided key - location pair(s) to Firestore. Returns an empty promise which is fulfilled when the write is complete. + * + * If any provided key already exists in this GeoFirestore, it will be overwritten with the new location value. + * + * @param keyOrLocations The key representing the location to add or a mapping of key - location pairs which + * represent the locations to add. + * @param location The [latitude, longitude] pair to add. + * @returns A promise that is fulfilled when the write is complete. + */ + public set(keyOrLocations: string | any, location?: number[]): Promise { + const batch: firebase.firestore.WriteBatch = this._collectionRef.firestore.batch(); + let locations; + if (typeof keyOrLocations === 'string' && keyOrLocations.length !== 0) { + // If this is a set for a single location, convert it into a object + locations = {}; + locations[keyOrLocations] = location; + } else if (typeof keyOrLocations === 'object') { + if (typeof location !== 'undefined') { + throw new Error('The location argument should not be used if you pass an object to set().'); + } + locations = keyOrLocations; + } else { + throw new Error('keyOrLocations must be a string or a mapping of key - location pairs.'); + } + + Object.keys(locations).forEach((key) => { + validateKey(key); + + const ref = this._collectionRef.doc(key); + const location: number[] = locations[key]; + if (location === null) { + batch.delete(ref); + } else { + validateLocation(location); + + const geohash: string = encodeGeohash(location); + batch.set(ref, encodeGeoFireObject(location, geohash), { merge: true }); + } + }); + + return batch.commit(); + }; + + /** + * Returns a new GeoQuery instance with the provided queryCriteria. + * + * @param queryCriteria The criteria which specifies the GeoQuery's center and radius. + * @return A new GeoFirestoreQuery object. + */ + public query(queryCriteria: QueryCriteria): GeoFirestoreQuery { + return new GeoFirestoreQuery(this._collectionRef, queryCriteria); + }; + + /********************/ + /* STATIC METHODS */ + /********************/ + /** + * Static method which calculates the distance, in kilometers, between two locations, + * via the Haversine formula. Note that this is approximate due to the fact that the + * Earth's radius varies between 6356.752 km and 6378.137 km. + * + * @param location1 The [latitude, longitude] pair of the first location. + * @param location2 The [latitude, longitude] pair of the second location. + * @returns The distance, in kilometers, between the inputted locations. + */ + static distance(location1: number[], location2: number[]) { + validateLocation(location1); + validateLocation(location2); + + var radius = 6371; // Earth's radius in kilometers + var latDelta = degreesToRadians(location2[0] - location1[0]); + var lonDelta = degreesToRadians(location2[1] - location1[1]); + + var a = (Math.sin(latDelta / 2) * Math.sin(latDelta / 2)) + + (Math.cos(degreesToRadians(location1[0])) * Math.cos(degreesToRadians(location2[0])) * + Math.sin(lonDelta / 2) * Math.sin(lonDelta / 2)); + + var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + + return radius * c; + }; +} diff --git a/src/geoFirestore/query.ts b/src/geoFirestore/query.ts new file mode 100644 index 00000000..5900a846 --- /dev/null +++ b/src/geoFirestore/query.ts @@ -0,0 +1,532 @@ +import * as firebase from 'firebase'; + +import { GeoFirestore } from './'; +import { GeoCallbackRegistration } from '../tools/callbackRegistration'; +import { decodeGeoFireObject, encodeGeohash, geoFirestoreGetKey, geohashQueries, validateCriteria, validateLocation } from '../tools/utils'; + +import { QueryCriteria, GeoFireObj, GeoFirestoreQueryState } from '../interfaces'; + +/** + * Creates a GeoFirestoreQuery instance. + */ +export class GeoFirestoreQuery { + // Event callbacks + private _callbacks: any = { ready: [], key_entered: [], key_exited: [], key_moved: [] }; + // Variable to track when the query is cancelled + private _cancelled: boolean = false; + private _center: number[]; + // A dictionary of geohash queries which currently have an active callbacks + private _currentGeohashesQueried: any = {}; + // A dictionary of locations that a currently active in the queries + // Note that not all of these are currently within this query + private _locationsTracked: any = {}; + private _radius: number; + + // Variables used to keep track of when to fire the 'ready' event + private _valueEventFired: boolean = false; + private _outstandingGeohashReadyEvents: any; + // Every ten seconds, clean up the geohashes we are currently querying for. We keep these around + // for a little while since it's likely that they will need to be re-queried shortly after they + // move outside of the query's bounding box. + private _geohashCleanupScheduled: boolean = false; + private _cleanUpCurrentGeohashesQueriedInterval: NodeJS.Timer; + private _cleanUpCurrentGeohashesQueriedTimeout = null; + + /** + * @param _collectionRef A Firestore Collection reference where the GeoFirestore data will be stored. + * @param _queryCriteria The criteria which specifies the query's center and radius. + */ + constructor(private _collectionRef: firebase.firestore.CollectionReference, private _queryCriteria: QueryCriteria) { + // Firebase reference of the GeoFirestore which created this query + if (Object.prototype.toString.call(this._collectionRef) !== '[object Object]') { + throw new Error('firebaseRef must be an instance of Firestore'); + } + + this._cleanUpCurrentGeohashesQueriedInterval = setInterval(() => { + if (this._geohashCleanupScheduled === false) { + this._cleanUpCurrentGeohashesQueried(); + } + }, 10000); + + // Validate and save the query criteria + validateCriteria(_queryCriteria, true); + this._center = _queryCriteria.center; + this._radius = _queryCriteria.radius; + + // Listen for new geohashes being added around this query and fire the appropriate events + this._listenForNewGeohashes(); + } + + /********************/ + /* PUBLIC METHODS */ + /********************/ + /** + * Terminates this query so that it no longer sends location updates. All callbacks attached to this + * query via on() will be cancelled. This query can no longer be used in the future. + */ + public cancel(): void { + // Mark this query as cancelled + this._cancelled = true; + + // Cancel all callbacks in this query's callback list + this._callbacks = { ready: [], key_entered: [], key_exited: [], key_moved: [] }; + + // Turn off all Firebase listeners for the current geohashes being queried + const keys: string[] = Object.keys(this._currentGeohashesQueried); + keys.forEach((geohashQueryStr: string) => { + const query: string[] = this._stringToQuery(geohashQueryStr); + this._cancelGeohashQuery(query, this._currentGeohashesQueried[geohashQueryStr]); + delete this._currentGeohashesQueried[geohashQueryStr]; + }); + + // Delete any stored locations + this._locationsTracked = {}; + + // Turn off the current geohashes queried clean up interval + clearInterval(this._cleanUpCurrentGeohashesQueriedInterval); + }; + + /** + * Returns the location signifying the center of this query. + * + * @returns The [latitude, longitude] pair signifying the center of this query. + */ + public center(): number[] { + return this._center; + }; + + /** + * Attaches a callback to this query which will be run when the provided eventType fires. Valid eventType + * values are 'ready', 'key_entered', 'key_exited', and 'key_moved'. The ready event callback is passed no + * parameters. All other callbacks will be passed three parameters: (1) the location's key, (2) the location's + * [latitude, longitude] pair, and (3) the distance, in kilometers, from the location to this query's center + * + * 'ready' is used to signify that this query has loaded its initial state and is up-to-date with its corresponding + * GeoFirestore instance. 'ready' fires when this query has loaded all of the initial data from GeoFirestore and fired all + * other events for that data. It also fires every time updateCriteria() is called, after all other events have + * fired for the updated query. + * + * 'key_entered' fires when a key enters this query. This can happen when a key moves from a location outside of + * this query to one inside of it or when a key is written to GeoFirestore for the first time and it falls within + * this query. + * + * 'key_exited' fires when a key moves from a location inside of this query to one outside of it. If the key was + * entirely removed from GeoFire, both the location and distance passed to the callback will be null. + * + * 'key_moved' fires when a key which is already in this query moves to another location inside of it. + * + * Returns a GeoCallbackRegistration which can be used to cancel the callback. You can add as many callbacks + * as you would like for the same eventType by repeatedly calling on(). Each one will get called when its + * corresponding eventType fires. Each callback must be cancelled individually. + * + * @param eventType The event type for which to attach the callback. One of 'ready', 'key_entered', + * 'key_exited', or 'key_moved'. + * @param callback Callback function to be called when an event of type eventType fires. + * @returns A callback registration which can be used to cancel the provided callback. + */ + public on(eventType: string, callback: Function): GeoCallbackRegistration { + // Validate the inputs + if (['ready', 'key_entered', 'key_exited', 'key_moved'].indexOf(eventType) === -1) { + throw new Error('event type must be \'ready\', \'key_entered\', \'key_exited\', or \'key_moved\''); + } + if (typeof callback !== 'function') { + throw new Error('callback must be a function'); + } + + // Add the callback to this query's callbacks list + this._callbacks[eventType].push(callback); + + // If this is a 'key_entered' callback, fire it for every location already within this query + if (eventType === 'key_entered') { + const keys: string[] = Object.keys(this._locationsTracked); + keys.forEach((key: string) => { + const locationDict = this._locationsTracked[key]; + if (typeof locationDict !== 'undefined' && locationDict.isInQuery) { + callback(key, locationDict.location, locationDict.distanceFromCenter); + } + }); + } + + // If this is a 'ready' callback, fire it if this query is already ready + if (eventType === 'ready' && this._valueEventFired) { + callback(); + } + + // Return an event registration which can be used to cancel the callback + return new GeoCallbackRegistration(() => { + this._callbacks[eventType].splice(this._callbacks[eventType].indexOf(callback), 1); + }); + }; + + /** + * Returns the radius of this query, in kilometers. + * + * @returns The radius of this query, in kilometers. + */ + public radius(): number { + return this._radius; + }; + + /** + * Updates the criteria for this query. + * + * @param newQueryCriteria The criteria which specifies the query's center and radius. + */ + public updateCriteria(newQueryCriteria: QueryCriteria): void { + // Validate and save the new query criteria + validateCriteria(newQueryCriteria); + this._center = newQueryCriteria.center || this._center; + this._radius = newQueryCriteria.radius || this._radius; + + // Loop through all of the locations in the query, update their distance from the center of the + // query, and fire any appropriate events + const keys: string[] = Object.keys(this._locationsTracked); + for (const key of keys) { + // If the query was cancelled while going through this loop, stop updating locations and stop + // firing events + if (this._cancelled === true) { + break; + } + // Get the cached information for this location + const locationDict = this._locationsTracked[key]; + // Save if the location was already in the query + const wasAlreadyInQuery = locationDict.isInQuery; + // Update the location's distance to the new query center + locationDict.distanceFromCenter = GeoFirestore.distance(locationDict.location, this._center); + // Determine if the location is now in this query + locationDict.isInQuery = (locationDict.distanceFromCenter <= this._radius); + // If the location just left the query, fire the 'key_exited' callbacks + // Else if the location just entered the query, fire the 'key_entered' callbacks + if (wasAlreadyInQuery && !locationDict.isInQuery) { + this._fireCallbacksForKey('key_exited', key, locationDict.location, locationDict.distanceFromCenter); + } else if (!wasAlreadyInQuery && locationDict.isInQuery) { + this._fireCallbacksForKey('key_entered', key, locationDict.location, locationDict.distanceFromCenter); + } + } + + // Reset the variables which control when the 'ready' event fires + this._valueEventFired = false; + + // Listen for new geohashes being added to GeoFirestore and fire the appropriate events + this._listenForNewGeohashes(); + }; + + + /*********************/ + /* PRIVATE METHODS */ + /*********************/ + /** + * Turns off all callbacks for the provide geohash query. + * + * @param query The geohash query. + * @param queryState An object storing the current state of the query. + */ + private _cancelGeohashQuery(query: string[], queryState: GeoFirestoreQueryState): void { + queryState.childCallback(); + queryState.valueCallback(); + } + + /** + * Callback for child added events. + * + * @param locationDataSnapshot A snapshot of the data stored for this location. + */ + private _childAddedCallback(locationDataSnapshot: firebase.firestore.DocumentSnapshot): void { + this._updateLocation(geoFirestoreGetKey(locationDataSnapshot), decodeGeoFireObject(locationDataSnapshot.data())); + } + + /** + * Callback for child changed events + * + * @param locationDataSnapshot A snapshot of the data stored for this location. + */ + private _childChangedCallback(locationDataSnapshot: firebase.firestore.DocumentSnapshot): void { + this._updateLocation(geoFirestoreGetKey(locationDataSnapshot), decodeGeoFireObject(locationDataSnapshot.data())); + } + + /** + * Callback for child removed events + * + * @param locationDataSnapshot A snapshot of the data stored for this location. + */ + private _childRemovedCallback(locationDataSnapshot: firebase.firestore.DocumentSnapshot): void { + const key: string = geoFirestoreGetKey(locationDataSnapshot); + if (this._locationsTracked.hasOwnProperty(key)) { + this._collectionRef.doc(key).get().then((snapshot: firebase.firestore.DocumentSnapshot) => { + const location: number[] = (snapshot.data() === null) ? null : decodeGeoFireObject(snapshot.data()); + const geohash: string = (location !== null) ? encodeGeohash(location) : null; + // Only notify observers if key is not part of any other geohash query or this actually might not be + // a key exited event, but a key moved or entered event. These events will be triggered by updates + // to a different query + if (!this._geohashInSomeQuery(geohash)) { + this._removeLocation(key, location); + } + }); + } + } + + /** + * Removes unnecessary Firebase queries which are currently being queried. + */ + private _cleanUpCurrentGeohashesQueried(): void { + let keys: string[] = Object.keys(this._currentGeohashesQueried); + keys.forEach((geohashQueryStr: string) => { + const queryState: any = this._currentGeohashesQueried[geohashQueryStr]; + if (queryState.active === false) { + var query = this._stringToQuery(geohashQueryStr); + // Delete the geohash since it should no longer be queried + this._cancelGeohashQuery(query, queryState); + delete this._currentGeohashesQueried[geohashQueryStr]; + } + }); + + // Delete each location which should no longer be queried + keys = Object.keys(this._locationsTracked); + keys.forEach((key: string) => { + if (!this._geohashInSomeQuery(this._locationsTracked[key].geohash)) { + if (this._locationsTracked[key].isInQuery) { + throw new Error('Internal State error, trying to remove location that is still in query'); + } + delete this._locationsTracked[key]; + } + }); + + // Specify that this is done cleaning up the current geohashes queried + this._geohashCleanupScheduled = false; + + // Cancel any outstanding scheduled cleanup + if (this._cleanUpCurrentGeohashesQueriedTimeout !== null) { + clearTimeout(this._cleanUpCurrentGeohashesQueriedTimeout); + this._cleanUpCurrentGeohashesQueriedTimeout = null; + } + } + + /** + * Fires each callback for the provided eventType, passing it provided key's data. + * + * @param eventType The event type whose callbacks to fire. One of 'key_entered', 'key_exited', or 'key_moved'. + * @param key The key of the location for which to fire the callbacks. + * @param location The location as [latitude, longitude] pair + * @param distanceFromCenter The distance from the center or null. + */ + private _fireCallbacksForKey(eventType: string, key: string, location?: number[], distanceFromCenter?: number): void { + this._callbacks[eventType].forEach((callback) => { + if (typeof location === 'undefined' || location === null) { + callback(key, null, null); + } else { + callback(key, location, distanceFromCenter); + } + }); + } + + /** + * Fires each callback for the 'ready' event. + */ + private _fireReadyEventCallbacks(): void { + this._callbacks.ready.forEach((callback) => { + callback(); + }); + } + + /** + * Checks if this geohash is currently part of any of the geohash queries. + * + * @param geohash The geohash. + * @returns Returns true if the geohash is part of any of the current geohash queries. + */ + private _geohashInSomeQuery(geohash: string): boolean { + const keys: string[] = Object.keys(this._currentGeohashesQueried); + for (const queryStr of keys) { + if (this._currentGeohashesQueried.hasOwnProperty(queryStr)) { + var query = this._stringToQuery(queryStr); + if (geohash >= query[0] && geohash <= query[1]) { + return true; + } + } + } + + return false; + } + + /** + * Called once all geohash queries have received all child added events and fires the ready + * event if necessary. + */ + private _geohashQueryReadyCallback(queryStr?: string): void { + const index: number = this._outstandingGeohashReadyEvents.indexOf(queryStr); + if (index > -1) { + this._outstandingGeohashReadyEvents.splice(index, 1); + } + this._valueEventFired = (this._outstandingGeohashReadyEvents.length === 0); + + // If all queries have been processed, fire the ready event + if (this._valueEventFired) { + this._fireReadyEventCallbacks(); + } + } + + /** + * Attaches listeners to Firebase which track when new geohashes are added within this query's + * bounding box. + */ + private _listenForNewGeohashes(): void { + // Get the list of geohashes to query + let geohashesToQuery: string[] = geohashQueries(this._center, this._radius * 1000).map(this._queryToString); + + // Filter out duplicate geohashes + geohashesToQuery = geohashesToQuery.filter((geohash: string, i: number) => geohashesToQuery.indexOf(geohash) === i); + + // For all of the geohashes that we are already currently querying, check if they are still + // supposed to be queried. If so, don't re-query them. Otherwise, mark them to be un-queried + // next time we clean up the current geohashes queried dictionary. + const keys: string[] = Object.keys(this._currentGeohashesQueried); + keys.forEach((geohashQueryStr: string) => { + const index: number = geohashesToQuery.indexOf(geohashQueryStr); + if (index === -1) { + this._currentGeohashesQueried[geohashQueryStr].active = false; + } else { + this._currentGeohashesQueried[geohashQueryStr].active = true; + geohashesToQuery.splice(index, 1); + } + }); + + // If we are not already cleaning up the current geohashes queried and we have more than 25 of them, + // kick off a timeout to clean them up so we don't create an infinite number of unneeded queries. + if (this._geohashCleanupScheduled === false && Object.keys(this._currentGeohashesQueried).length > 25) { + this._geohashCleanupScheduled = true; + this._cleanUpCurrentGeohashesQueriedTimeout = setTimeout(this._cleanUpCurrentGeohashesQueried, 10); + } + + // Keep track of which geohashes have been processed so we know when to fire the 'ready' event + this._outstandingGeohashReadyEvents = geohashesToQuery.slice(); + + // Loop through each geohash to query for and listen for new geohashes which have the same prefix. + // For every match, attach a value callback which will fire the appropriate events. + // Once every geohash to query is processed, fire the 'ready' event. + geohashesToQuery.forEach((toQueryStr: string) => { + // decode the geohash query string + const query: string[] = this._stringToQuery(toQueryStr); + + // Create the Firebase query + const firestoreQuery: firebase.firestore.Query = this._collectionRef.orderBy('g').startAt(query[0]).endAt(query[1]); + + // For every new matching geohash, determine if we should fire the 'key_entered' event + const childCallback = firestoreQuery.onSnapshot((snapshot: firebase.firestore.QuerySnapshot) => { + snapshot.docChanges.forEach((change: firebase.firestore.DocumentChange) => { + if (change.type === 'added') { + this._childAddedCallback(change.doc); + } + if (change.type === 'modified') { + this._childChangedCallback(change.doc); + } + if (change.type === 'removed') { + this._childRemovedCallback(change.doc); + } + }); + }); + + // Once the current geohash to query is processed, see if it is the last one to be processed + // and, if so, mark the value event as fired. + // Note that Firebase fires the 'value' event after every 'added' event fires. + const valueCallback = firestoreQuery.onSnapshot(() => { + valueCallback(); + this._geohashQueryReadyCallback(toQueryStr); + }); + + // Add the geohash query to the current geohashes queried dictionary and save its state + this._currentGeohashesQueried[toQueryStr] = { + active: true, + childCallback: childCallback, + valueCallback: valueCallback + }; + }); + // Based upon the algorithm to calculate geohashes, it's possible that no 'new' + // geohashes were queried even if the client updates the radius of the query. + // This results in no 'READY' event being fired after the .updateCriteria() call. + // Check to see if this is the case, and trigger the 'READY' event. + if (geohashesToQuery.length === 0) { + this._geohashQueryReadyCallback(); + } + } + + /** + * Encodes a query as a string for easier indexing and equality. + * + * @param query The query to encode. + * @returns The encoded query as string. + */ + private _queryToString(query: string[]): string { + if (query.length !== 2) { + throw new Error('Not a valid geohash query: ' + query); + } + return query[0] + ':' + query[1]; + } + + /** + * Removes the location from the local state and fires any events if necessary. + * + * @param key The key to be removed. + * @param currentLocation The current location as [latitude, longitude] pair or null if removed. + */ + private _removeLocation(key: string, currentLocation?: number[]): void { + const locationDict = this._locationsTracked[key]; + delete this._locationsTracked[key]; + if (typeof locationDict !== 'undefined' && locationDict.isInQuery) { + const distanceFromCenter: number = (currentLocation) ? GeoFirestore.distance(currentLocation, this._center) : null; + this._fireCallbacksForKey('key_exited', key, currentLocation, distanceFromCenter); + } + } + + /** + * Decodes a query string to a query + * + * @param str The encoded query. + * @returns The decoded query as a [start, end] pair. + */ + private _stringToQuery(str: string): string[] { + const decoded: string[] = str.split(':'); + if (decoded.length !== 2) { + throw new Error('Invalid internal state! Not a valid geohash query: ' + str); + } + return decoded; + } + + /** + * Callback for any updates to locations. Will update the information about a key and fire any necessary + * events every time the key's location changes. + * + * When a key is removed from GeoFirestore or the query, this function will be called with null and performs + * any necessary cleanup. + * + * @param key The key of the GeoFirestore location. + * @param location The location as [latitude, longitude] pair. + */ + private _updateLocation(key: string, location?: number[]): void { + validateLocation(location); + // Get the key and location + let distanceFromCenter: number, isInQuery; + var wasInQuery: boolean = (this._locationsTracked.hasOwnProperty(key)) ? this._locationsTracked[key].isInQuery : false; + var oldLocation: number[] = (this._locationsTracked.hasOwnProperty(key)) ? this._locationsTracked[key].location : null; + + // Determine if the location is within this query + distanceFromCenter = GeoFirestore.distance(location, this._center); + isInQuery = (distanceFromCenter <= this._radius); + + // Add this location to the locations queried dictionary even if it is not within this query + this._locationsTracked[key] = { + location: location, + distanceFromCenter: distanceFromCenter, + isInQuery: isInQuery, + geohash: encodeGeohash(location) + }; + + // Fire the 'key_entered' event if the provided key has entered this query + if (isInQuery && !wasInQuery) { + this._fireCallbacksForKey('key_entered', key, location, distanceFromCenter); + } else if (isInQuery && oldLocation !== null && (location[0] !== oldLocation[0] || location[1] !== oldLocation[1])) { + this._fireCallbacksForKey('key_moved', key, location, distanceFromCenter); + } else if (!isInQuery && wasInQuery) { + this._fireCallbacksForKey('key_exited', key, location, distanceFromCenter); + } + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 48c7480c..e9b19b41 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,3 @@ -/*! - * GeoFire is an open-source library that allows you to store and query a set - * of keys based on their geographic location. At its heart, GeoFire simply - * stores locations with string keys. Its main benefit, however, is the - * possibility of retrieving only those keys within a given geographic area - - * all in realtime. - * - * GeoFire 0.0.0 - * https://github.com/firebase/geofire-js/ - * License: MIT - */ - -export { GeoFire } from './firebase/geoFire'; \ No newline at end of file +export * from './interfaces'; +export { GeoFire } from './geoFire'; +export { GeoFirestore } from './geoFirestore'; \ No newline at end of file diff --git a/src/interfaces/geoFirestoreQueryState.ts b/src/interfaces/geoFirestoreQueryState.ts new file mode 100644 index 00000000..8fa27cae --- /dev/null +++ b/src/interfaces/geoFirestoreQueryState.ts @@ -0,0 +1,5 @@ +export interface GeoFirestoreQueryState { + active: boolean; + childCallback: Function; + valueCallback: Function; +} \ No newline at end of file diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index 1c3818cc..709e52ea 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -1,2 +1,3 @@ export * from './geoFireObj'; +export * from './geoFirestoreQueryState'; export * from './queryCriteria'; \ No newline at end of file diff --git a/src/firebase/geoCallbackRegistration.ts b/src/tools/callbackRegistration.ts similarity index 100% rename from src/firebase/geoCallbackRegistration.ts rename to src/tools/callbackRegistration.ts diff --git a/src/firebase/geoFireUtils.ts b/src/tools/utils.ts similarity index 96% rename from src/firebase/geoFireUtils.ts rename to src/tools/utils.ts index 849a6abd..40c73e19 100644 --- a/src/firebase/geoFireUtils.ts +++ b/src/tools/utils.ts @@ -426,12 +426,26 @@ export function decodeGeoFireObject(geoFireObj: GeoFireObj): number[] { * Returns the key of a Firebase snapshot across SDK versions. * * @param A Firebase snapshot. - * @returns key The Firebase snapshot's key. + * @returns The Firebase snapshot's key. */ -export function getKey(snapshot: firebase.database.DataSnapshot): string { +export function geoFireGetKey(snapshot: firebase.database.DataSnapshot): string { let key: string; if (typeof snapshot.key === 'string' || snapshot.key === null) { key = snapshot.key; } return key; +} + +/** + * Returns the id of a Firestore snapshot across SDK versions. + * + * @param A Firestore snapshot. + * @returns The Firestore snapshot's id. + */ +export function geoFirestoreGetKey(snapshot: firebase.firestore.DocumentSnapshot): string { + let id: string; + if (typeof snapshot.id === 'string' || snapshot.id === null) { + id = snapshot.id; + } + return id; } \ No newline at end of file diff --git a/tests/common.ts b/tests/common.ts index 1c476a79..76a1f749 100644 --- a/tests/common.ts +++ b/tests/common.ts @@ -46,9 +46,9 @@ export function beforeEachHelper(done) { /* Helper function which runs after each Jasmine test has completed */ export function afterEachHelper(done) { - // Cancel each outstanding GeoQuery - geoQueries.forEach(function (geoQuery) { - geoQuery.cancel(); + // Cancel each outstanding GeoFireQuery + geoQueries.forEach(function (geoFireQuery) { + geoFireQuery.cancel(); }) geoFireRef.remove().then(function () { diff --git a/tests/geoFire.test.ts b/tests/geoFire/index.test.ts similarity index 99% rename from tests/geoFire.test.ts rename to tests/geoFire/index.test.ts index 35743db1..a3a59525 100755 --- a/tests/geoFire.test.ts +++ b/tests/geoFire/index.test.ts @@ -1,11 +1,11 @@ import * as chai from 'chai'; -import { GeoFire } from '../src/firebase/geoFire'; -import { GeoQuery } from '../src/firebase/geoQuery'; +import { GeoFire } from '../../src/geoFire'; +import { GeoFireQuery } from '../../src/geoFire/query'; import { afterEachHelper, beforeEachHelper, Checklist, failTestOnCaughtError, geoFire, geoFireRef, getFirebaseData, geoQueries, invalidFirebaseRefs, invalidKeys, invalidLocations, invalidQueryCriterias, validKeys, validLocations, validQueryCriterias -} from './common'; +} from '../common'; const expect = chai.expect; @@ -819,10 +819,10 @@ describe('GeoFire Tests:', () => { }); describe('query():', () => { - it('query() returns GeoQuery instance', () => { + it('query() returns GeoFireQuery instance', () => { geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - expect(geoQueries[0] instanceof GeoQuery).to.be.ok; + expect(geoQueries[0] instanceof GeoFireQuery).to.be.ok; }); it('query() does not throw errors given valid query criteria', () => { diff --git a/tests/geoQuery.test.ts b/tests/geoFire/query.test.ts similarity index 95% rename from tests/geoQuery.test.ts rename to tests/geoFire/query.test.ts index dfa4eeb9..fb5895ae 100644 --- a/tests/geoQuery.test.ts +++ b/tests/geoFire/query.test.ts @@ -2,11 +2,11 @@ import * as chai from 'chai'; import { afterEachHelper, beforeEachHelper, Checklist, failTestOnCaughtError, geoFire, geoQueries, invalidQueryCriterias, validQueryCriterias, wait -} from './common'; +} from '../common'; const expect = chai.expect; -describe('GeoQuery Tests:', () => { +describe('GeoFireQuery Tests:', () => { // Reset the Firebase before each test beforeEach((done) => { beforeEachHelper(done); @@ -84,7 +84,7 @@ describe('GeoQuery Tests:', () => { expect(geoQueries[0].radius()).to.equal(100); }); - it('updateCriteria() fires \'key_entered\' callback for locations which now belong to the GeoQuery', (done) => { + it('updateCriteria() fires \'key_entered\' callback for locations which now belong to the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); geoQueries.push(geoFire.query({ center: [90, 90], radius: 1000 })); @@ -109,7 +109,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('updateCriteria() fires \'key_entered\' callback for locations with complex keys which now belong to the GeoQuery', (done) => { + it('updateCriteria() fires \'key_entered\' callback for locations with complex keys which now belong to the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'loc:^:*1 entered', 'loc-+-+-4 entered'], expect, done); geoQueries.push(geoFire.query({ center: [90, 90], radius: 1000 })); @@ -134,7 +134,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('updateCriteria() fires \'key_exited\' callback for locations which no longer belong to the GeoQuery', (done) => { + it('updateCriteria() fires \'key_exited\' callback for locations which no longer belong to the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 exited', 'loc4 exited'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -455,7 +455,7 @@ describe('GeoQuery Tests:', () => { }); describe('\'key_moved\' event:', () => { - it('\'key_moved\' callback does not fire for brand new locations within or outside of the GeoQuery', (done) => { + it('\'key_moved\' callback does not fire for brand new locations within or outside of the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -477,7 +477,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback does not fire for locations outside of the GeoQuery which are moved somewhere else outside of the GeoQuery', (done) => { + it('\'key_moved\' callback does not fire for locations outside of the GeoFireQuery which are moved somewhere else outside of the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -506,7 +506,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback does not fire for locations outside of the GeoQuery which are moved within the GeoQuery', (done) => { + it('\'key_moved\' callback does not fire for locations outside of the GeoFireQuery which are moved within the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -535,7 +535,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback does not fire for locations within the GeoQuery which are moved somewhere outside of the GeoQuery', (done) => { + it('\'key_moved\' callback does not fire for locations within the GeoFireQuery which are moved somewhere outside of the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -564,7 +564,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback does not fires for a location within the GeoQuery which is set to the same location', (done) => { + it('\'key_moved\' callback does not fires for a location within the GeoFireQuery which is set to the same location', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc3 moved'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -594,7 +594,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback fires for locations within the GeoQuery which are moved somewhere else within the GeoQuery', (done) => { + it('\'key_moved\' callback fires for locations within the GeoFireQuery which are moved somewhere else within the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -681,7 +681,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback properly fires when multiple keys are at the same location within the GeoQuery and only one of them moves somewhere else within the GeoQuery', (done) => { + it('\'key_moved\' callback properly fires when multiple keys are at the same location within the GeoFireQuery and only one of them moves somewhere else within the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -710,7 +710,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback properly fires when a location within the GeoQuery moves somehwere else within the GeoQuery that is already occupied by another key', (done) => { + it('\'key_moved\' callback properly fires when a location within the GeoFireQuery moves somehwere else within the GeoFireQuery that is already occupied by another key', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -739,7 +739,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('multiple \'key_moved\' callbacks fire for locations within the GeoQuery which are moved somewhere else within the GeoQuery', (done) => { + it('multiple \'key_moved\' callbacks fire for locations within the GeoFireQuery which are moved somewhere else within the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved1', 'loc3 moved1', 'loc1 moved2', 'loc3 moved2'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -773,7 +773,7 @@ describe('GeoQuery Tests:', () => { }); describe('\'key_entered\' event:', () => { - it('\'key_entered\' callback fires when a location enters the GeoQuery before onKeyEntered() was called', (done) => { + it('\'key_entered\' callback fires when a location enters the GeoFireQuery before onKeyEntered() was called', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -797,7 +797,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_entered\' callback fires when a location enters the GeoQuery after onKeyEntered() was called', (done) => { + it('\'key_entered\' callback fires when a location enters the GeoFireQuery after onKeyEntered() was called', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -869,7 +869,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_entered\' callback properly fires when multiple keys are at the same location outside the GeoQuery and only one of them moves within the GeoQuery', (done) => { + it('\'key_entered\' callback properly fires when multiple keys are at the same location outside the GeoFireQuery and only one of them moves within the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 entered'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -895,7 +895,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_entered\' callback properly fires when a location outside the GeoQuery moves somewhere within the GeoQuery that is already occupied by another key', (done) => { + it('\'key_entered\' callback properly fires when a location outside the GeoFireQuery moves somewhere within the GeoFireQuery that is already occupied by another key', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 entered', 'loc3 entered'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -921,7 +921,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('multiple \'key_entered\' callbacks fire when a location enters the GeoQuery', (done) => { + it('multiple \'key_entered\' callbacks fire when a location enters the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 entered1', 'loc4 entered1', 'loc1 entered2', 'loc4 entered2'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -950,7 +950,7 @@ describe('GeoQuery Tests:', () => { }); describe('\'key_exited\' event:', () => { - it('\'key_exited\' callback fires when a location leaves the GeoQuery', (done) => { + it('\'key_exited\' callback fires when a location leaves the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited', 'loc4 exited'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -1069,7 +1069,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_exited\' callback fires when a location within the GeoQuery is entirely removed from GeoFire', (done) => { + it('\'key_exited\' callback fires when a location within the GeoFireQuery is entirely removed from GeoFire', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -1094,7 +1094,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_exited\' callback properly fires when multiple keys are at the same location inside the GeoQuery and only one of them moves outside the GeoQuery', (done) => { + it('\'key_exited\' callback properly fires when multiple keys are at the same location inside the GeoFireQuery and only one of them moves outside the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -1119,7 +1119,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_exited\' callback properly fires when a location inside the GeoQuery moves somewhere outside the GeoQuery that is already occupied by another key', (done) => { + it('\'key_exited\' callback properly fires when a location inside the GeoFireQuery moves somewhere outside the GeoFireQuery that is already occupied by another key', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -1145,7 +1145,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('multiple \'key_exited\' callbacks fire when a location leaves the GeoQuery', (done) => { + it('multiple \'key_exited\' callbacks fire when a location leaves the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited1', 'loc4 exited1', 'loc1 exited2', 'loc4 exited2'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -1259,8 +1259,8 @@ describe('GeoQuery Tests:', () => { }); }); - describe('Cancelling GeoQuery:', () => { - it('cancel() prevents GeoQuery from firing any more \'key_*\' event callbacks', (done) => { + describe('Cancelling GeoFireQuery:', () => { + it('cancel() prevents GeoFireQuery from firing any more \'key_*\' event callbacks', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 entered', 'loc4 entered', 'loc1 moved', 'loc4 exited'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); @@ -1323,7 +1323,7 @@ describe('GeoQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('Calling cancel() on one GeoQuery does not cancel other GeoQueries', (done) => { + it('Calling cancel() on one GeoFireQuery does not cancel other GeoQueries', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 entered1', 'loc1 entered2', 'loc4 entered1', 'loc4 entered2', 'loc1 moved1', 'loc1 moved2', 'loc4 exited1', 'loc4 exited2', 'loc1 exited2', 'loc5 entered2'], expect, done); geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); diff --git a/tests/geoCallbackRegistration.test.ts b/tests/tools/callbackRegistration.test.ts similarity index 98% rename from tests/geoCallbackRegistration.test.ts rename to tests/tools/callbackRegistration.test.ts index 892e8079..65e44453 100644 --- a/tests/geoCallbackRegistration.test.ts +++ b/tests/tools/callbackRegistration.test.ts @@ -1,8 +1,8 @@ -import { GeoCallbackRegistration } from '../src/firebase/geoCallbackRegistration'; +import { GeoCallbackRegistration } from '../../src/tools/callbackRegistration'; import { afterEachHelper, beforeEachHelper, Checklist, failTestOnCaughtError, geoFire, geoQueries, wait -} from './common'; +} from '../common'; import * as chai from 'chai'; diff --git a/tests/geoFireUtils.test.ts b/tests/tools/utils.test.ts similarity index 99% rename from tests/geoFireUtils.test.ts rename to tests/tools/utils.test.ts index c80f7300..d1da57e0 100644 --- a/tests/geoFireUtils.test.ts +++ b/tests/tools/utils.test.ts @@ -1,14 +1,14 @@ import * as chai from 'chai'; -import { GeoFire } from '../src/firebase/geoFire'; +import { GeoFire } from '../../src/geoFire'; import { boundingBoxBits, degreesToRadians, encodeGeohash, geohashQuery, geohashQueries, g_GEOHASH_PRECISION, metersToLongitudeDegrees, validateCriteria, validateGeohash, validateKey, validateLocation, wrapLongitude -} from '../src/firebase/geoFireUtils'; +} from '../../src/tools/utils'; import { invalidGeohashes, invalidKeys, invalidLocations, invalidQueryCriterias, validGeohashes, validKeys, validLocations, validQueryCriterias -} from './common'; +} from '../common'; const expect = chai.expect; From b73d800e48a0382465b7d7c1a28ddab448b7540b Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Fri, 9 Mar 2018 20:14:47 -0500 Subject: [PATCH 06/23] test(firestore): implement test against realtime db to firestore --- package.json | 2 +- src/geoFirestore/index.ts | 31 +- src/interfaces/queryCriteria.ts | 4 +- tests/common.ts | 96 +- tests/geoFire/index.test.ts | 10 +- tests/geoFire/query.test.ts | 317 +++---- tests/geoFirestore/index.test.ts | 851 ++++++++++++++++++ tests/geoFirestore/query.test.ts | 1421 ++++++++++++++++++++++++++++++ 8 files changed, 2544 insertions(+), 188 deletions(-) create mode 100755 tests/geoFirestore/index.test.ts create mode 100644 tests/geoFirestore/query.test.ts diff --git a/package.json b/package.json index b5b59ded..a935c4f3 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "scripts": { "browserify": "browserify dist/geoFire/index.js -o dist/browser/geofire.js && browserify dist/geoFirestore/index.js -o dist/browser/geofirestore.js", "build": "rm -rf ./dist && tsc && npm run browserify && npm run uglify", - "test": "mocha --reporter spec -r ts-node/register -r jsdom-global/register --timeout 5000 --exit 'tests/**/*.test.ts'", + "test": "mocha --reporter spec -r ts-node/register -r jsdom-global/register --timeout 10000 --exit 'tests/**/*.test.ts'", "travis": "npm run test && npm run build", "uglify": "uglifyjs dist/browser/geofire.js -c -m -o dist/browser/geofire.min.js && uglifyjs dist/browser/geofirestore.js -c -m -o dist/browser/geofirestore.min.js" } diff --git a/src/geoFirestore/index.ts b/src/geoFirestore/index.ts index 1301f9e3..c6d2ab4c 100644 --- a/src/geoFirestore/index.ts +++ b/src/geoFirestore/index.ts @@ -44,10 +44,10 @@ export class GeoFirestore { public get(key: string): Promise { validateKey(key); return this._collectionRef.doc(key).get().then((documentSnapshot: firebase.firestore.DocumentSnapshot) => { - const snapshotVal = documentSnapshot.data(); - if (snapshotVal === null) { + if (!documentSnapshot.exists) { return null; } else { + const snapshotVal = documentSnapshot.data(); return decodeGeoFireObject(snapshotVal); } }); @@ -70,7 +70,7 @@ export class GeoFirestore { * @param key The key of the location to remove. * @returns A promise that is fulfilled after the inputted key is removed. */ - public remove(key: string): Promise { + public remove(key: string): Promise { return this.set(key, null); }; @@ -84,37 +84,36 @@ export class GeoFirestore { * @param location The [latitude, longitude] pair to add. * @returns A promise that is fulfilled when the write is complete. */ - public set(keyOrLocations: string | any, location?: number[]): Promise { - const batch: firebase.firestore.WriteBatch = this._collectionRef.firestore.batch(); - let locations; - if (typeof keyOrLocations === 'string' && keyOrLocations.length !== 0) { - // If this is a set for a single location, convert it into a object - locations = {}; - locations[keyOrLocations] = location; + public set(keyOrLocations: string | any, location?: number[]): Promise { + if (typeof keyOrLocations === 'string' && location && location.length !== 0) { + validateKey(keyOrLocations); + validateLocation(location); + const geohash: string = encodeGeohash(location); + return this._collectionRef.doc(keyOrLocations).set(encodeGeoFireObject(location, geohash)); + } else if (typeof keyOrLocations === 'string') { + validateKey(keyOrLocations); + return this._collectionRef.doc(keyOrLocations).delete(); } else if (typeof keyOrLocations === 'object') { if (typeof location !== 'undefined') { throw new Error('The location argument should not be used if you pass an object to set().'); } - locations = keyOrLocations; } else { throw new Error('keyOrLocations must be a string or a mapping of key - location pairs.'); } - Object.keys(locations).forEach((key) => { + const batch: firebase.firestore.WriteBatch = this._collectionRef.firestore.batch(); + Object.keys(keyOrLocations).forEach((key) => { validateKey(key); - const ref = this._collectionRef.doc(key); - const location: number[] = locations[key]; + const location: number[] = keyOrLocations[key]; if (location === null) { batch.delete(ref); } else { validateLocation(location); - const geohash: string = encodeGeohash(location); batch.set(ref, encodeGeoFireObject(location, geohash), { merge: true }); } }); - return batch.commit(); }; diff --git a/src/interfaces/queryCriteria.ts b/src/interfaces/queryCriteria.ts index 3a1b5325..806b4bac 100644 --- a/src/interfaces/queryCriteria.ts +++ b/src/interfaces/queryCriteria.ts @@ -1,4 +1,4 @@ export interface QueryCriteria { - center: number[]; - radius: number; + center?: number[]; + radius?: number; } \ No newline at end of file diff --git a/tests/common.ts b/tests/common.ts index 76a1f749..607ed93f 100644 --- a/tests/common.ts +++ b/tests/common.ts @@ -1,7 +1,10 @@ import * as chai from 'chai'; import * as firebase from 'firebase'; +import 'firebase/firestore'; -import { GeoFire } from '../src'; +import { GeoFire, GeoFirestore } from '../src'; +import { GeoFireQuery } from '../src/geoFire/query'; +import { GeoFirestoreQuery } from '../src/geoFirestore/query'; /*************/ /* GLOBALS */ @@ -19,19 +22,25 @@ export const validQueryCriterias = [{ center: [0, 0], radius: 1000 }, { center: export const invalidQueryCriterias = [{}, { random: 100 }, { center: [91, 2], radius: 1000, random: 'a' }, { center: [91, 2], radius: 1000 }, { center: [1, -181], radius: 1000 }, { center: ['a', 2], radius: 1000 }, { center: [1, [1, 2]], radius: 1000 }, { center: [0, 0], radius: -1 }, { center: [null, 2], radius: 1000 }, { center: [1, undefined], radius: 1000 }, { center: [NaN, 0], radius: 1000 }, { center: [1, 2], radius: -10 }, { center: [1, 2], radius: 'text' }, { center: [1, 2], radius: [1, 2] }, { center: [1, 2], radius: null }, true, false, undefined, NaN, [], 'a', 1]; // Create global constiables to hold the Firebase and GeoFire constiables -export let geoFireRef, geoFire, geoQueries = []; +export let geoFireRef: firebase.database.Reference, + geoFire: GeoFire, + geoFireQueries: GeoFireQuery[] = []; +export let geoFirestoreRef: firebase.firestore.CollectionReference, + geoFirestore: GeoFirestore, + geoFirestoreQueries: GeoFirestoreQuery[] = []; // Initialize Firebase const config = { - apiKey: 'AIzaSyC5IcRccDo289TTRa3Y7qJIu8YPz3EnKAI', - databaseURL: 'https://geofire-9d0de.firebaseio.com' + apiKey: 'AIzaSyAWJ1lpvNJgAwFYR0TRWyySeaMswTKnDOU', + databaseURL: 'https://geofire-95a87.firebaseio.com', + projectId: 'geofire-95a87' }; firebase.initializeApp(config); /**********************/ /* HELPER FUNCTIONS */ /**********************/ -/* Helper function which runs before each Jasmine test has started */ +/* Helper functions which runs before each Jasmine test has started */ export function beforeEachHelper(done) { // Create a new Firebase database ref at a random node geoFireRef = firebase.database().ref().push(); @@ -39,19 +48,43 @@ export function beforeEachHelper(done) { geoFire = new GeoFire(geoFireRef); // Reset the GeoQueries - geoQueries = []; + geoFireQueries = []; done(); } -/* Helper function which runs after each Jasmine test has completed */ +export function beforeEachHelperFirestore(done) { + // Create a new Firebase database ref at a random node + geoFirestoreRef = firebase.firestore().collection('geofire'); + // Create a new GeoFire instance + geoFirestore = new GeoFirestore(geoFirestoreRef); + + // Reset the GeoQueries + geoFirestoreQueries = []; + + done(); +} + +/* Helper functions which runs after each Jasmine test has completed */ export function afterEachHelper(done) { // Cancel each outstanding GeoFireQuery - geoQueries.forEach(function (geoFireQuery) { + geoFireQueries.forEach((geoFireQuery) => { geoFireQuery.cancel(); - }) + }); - geoFireRef.remove().then(function () { + geoFireRef.remove().then(() => { + // Wait for 50 milliseconds after each test to give enough time for old query events to expire + return wait(50); + }).then(done); +} + +export function afterEachHelperFirestore(done) { + // Cancel each outstanding GeoFirestoreQuery + geoFirestoreQueries.forEach((geoFirestoreQuery) => { + geoFirestoreQuery.cancel(); + }); + + deleteCollection(geoFirestoreRef.firestore, 'geofire', 50).then(() => { // Wait for 50 milliseconds after each test to give enough time for old query events to expire return wait(50); }).then(done); @@ -72,16 +105,27 @@ export function generateRandomString() { /* Returns the current data in the Firebase */ export function getFirebaseData() { - return geoFireRef.once('value').then(function (dataSnapshot) { + return geoFireRef.once('value').then((dataSnapshot: firebase.database.DataSnapshot) => { return dataSnapshot.exportVal(); }); }; +/* Returns the current data in the Firestore */ +export function getFirestoreData() { + return geoFirestoreRef.get().then((querySnapshot: firebase.firestore.QuerySnapshot) => { + const data = {}; + querySnapshot.docs.map((val: firebase.firestore.QueryDocumentSnapshot) => { + data[val.id] = val.data(); + }); + return data; + }); +}; + /* Returns a promise which is fulfilled after the inputted number of milliseconds pass */ export function wait(milliseconds) { return new Promise(function (resolve) { - const timeout = window.setTimeout(function () { + const timeout = window.setTimeout(() => { window.clearTimeout(timeout); resolve(); }, milliseconds); @@ -125,3 +169,31 @@ export function Checklist(items, expect, done) { export function failTestOnCaughtError(error) { expect(error).to.throw(); } + +/* Used to purge Firestore collection. Used by afterEachHelperFirestore. */ +function deleteCollection(db: firebase.firestore.Firestore, collectionPath: string, batchSize: number) { + const collectionRef = db.collection(collectionPath); + const query: firebase.firestore.Query = collectionRef.limit(batchSize); + + return new Promise((resolve, reject) => deleteQueryBatch(db, query, batchSize, resolve, reject)); +} + +/* Actually purges Firestore collection recursively through batch function. */ +function deleteQueryBatch(db: firebase.firestore.Firestore, query: firebase.firestore.Query, batchSize: number, resolve: Function, reject: Function) { + query.get().then((snapshot) => { + // When there are no documents left, we are done + if (snapshot.size == 0) { return 0; } + + // Delete documents in a batch + const batch = db.batch(); + snapshot.docs.forEach((doc) => batch.delete(doc.ref)); + + return batch.commit().then(() => snapshot.size); + }).then((numDeleted) => { + if (numDeleted === 0) { + resolve(); + return; + } + process.nextTick(() => deleteQueryBatch(db, query, batchSize, resolve, reject)); + }).catch(err => reject(err)); +} \ No newline at end of file diff --git a/tests/geoFire/index.test.ts b/tests/geoFire/index.test.ts index a3a59525..818058b1 100755 --- a/tests/geoFire/index.test.ts +++ b/tests/geoFire/index.test.ts @@ -3,7 +3,7 @@ import * as chai from 'chai'; import { GeoFire } from '../../src/geoFire'; import { GeoFireQuery } from '../../src/geoFire/query'; import { - afterEachHelper, beforeEachHelper, Checklist, failTestOnCaughtError, geoFire, geoFireRef, getFirebaseData, geoQueries, + afterEachHelper, beforeEachHelper, Checklist, failTestOnCaughtError, geoFire, geoFireRef, getFirebaseData, geoFireQueries, invalidFirebaseRefs, invalidKeys, invalidLocations, invalidQueryCriterias, validKeys, validLocations, validQueryCriterias } from '../common'; @@ -271,6 +271,7 @@ describe('GeoFire Tests:', () => { // Setting location to null is valid since it will remove the key if (invalidLocation !== null) { expect(() => { + // @ts-ignore geoFire.set('loc', invalidLocation); }).to.throw(); } @@ -570,6 +571,7 @@ describe('GeoFire Tests:', () => { it('get() throws errors given invalid keys', () => { invalidKeys.forEach((invalidKey) => { + // @ts-ignore expect(() => geoFire.get(invalidKey)).to.throw(); }); }); @@ -813,6 +815,7 @@ describe('GeoFire Tests:', () => { it('remove() throws errors given invalid keys', () => { invalidKeys.forEach((invalidKey) => { + // @ts-ignore expect(() => geoFire.remove(invalidKey)).to.throw(); }); }); @@ -820,9 +823,9 @@ describe('GeoFire Tests:', () => { describe('query():', () => { it('query() returns GeoFireQuery instance', () => { - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - expect(geoQueries[0] instanceof GeoFireQuery).to.be.ok; + expect(geoFireQueries[0] instanceof GeoFireQuery).to.be.ok; }); it('query() does not throw errors given valid query criteria', () => { @@ -835,6 +838,7 @@ describe('GeoFire Tests:', () => { it('query() throws errors given invalid query criteria', () => { invalidQueryCriterias.forEach((invalidQueryCriteria) => { + // @ts-ignore expect(() => geoFire.query(invalidQueryCriteria)).to.throw(); }); }); diff --git a/tests/geoFire/query.test.ts b/tests/geoFire/query.test.ts index fb5895ae..f4a3f23c 100644 --- a/tests/geoFire/query.test.ts +++ b/tests/geoFire/query.test.ts @@ -1,7 +1,7 @@ import * as chai from 'chai'; import { - afterEachHelper, beforeEachHelper, Checklist, failTestOnCaughtError, geoFire, geoQueries, invalidQueryCriterias, validQueryCriterias, wait + afterEachHelper, beforeEachHelper, Checklist, failTestOnCaughtError, geoFire, geoFireQueries, invalidQueryCriterias, validQueryCriterias, wait } from '../common'; const expect = chai.expect; @@ -18,77 +18,84 @@ describe('GeoFireQuery Tests:', () => { describe('Constructor:', () => { it('Constructor stores query criteria', () => { - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - expect(geoQueries[0].center()).to.deep.equal([1, 2]); - expect(geoQueries[0].radius()).to.equal(1000); + expect(geoFireQueries[0].center()).to.deep.equal([1, 2]); + expect(geoFireQueries[0].radius()).to.equal(1000); }); it('Constructor throws error on invalid query criteria', () => { expect(() => geoFire.query({})).to.throw(); + // @ts-ignore expect(() => geoFire.query({ random: 100 })).to.throw(); expect(() => geoFire.query({ center: [1, 2] })).to.throw(); expect(() => geoFire.query({ radius: 1000 })).to.throw(); expect(() => geoFire.query({ center: [91, 2], radius: 1000 })).to.throw(); expect(() => geoFire.query({ center: [1, -181], radius: 1000 })).to.throw(); + // @ts-ignore expect(() => geoFire.query({ center: ['text', 2], radius: 1000 })).to.throw(); + // @ts-ignore expect(() => geoFire.query({ center: [1, [1, 2]], radius: 1000 })).to.throw(); + // @ts-ignore expect(() => geoFire.query({ center: 1000, radius: 1000 })).to.throw(); expect(() => geoFire.query({ center: null, radius: 1000 })).to.throw(); expect(() => geoFire.query({ center: undefined, radius: 1000 })).to.throw(); expect(() => geoFire.query({ center: [null, 2], radius: 1000 })).to.throw(); expect(() => geoFire.query({ center: [1, undefined], radius: 1000 })).to.throw(); expect(() => geoFire.query({ center: [1, 2], radius: -10 })).to.throw(); + // @ts-ignore expect(() => geoFire.query({ center: [1, 2], radius: 'text' })).to.throw(); + // @ts-ignore expect(() => geoFire.query({ center: [1, 2], radius: [1, 2] })).to.throw(); expect(() => geoFire.query({ center: [1, 2], radius: null })).to.throw(); expect(() => geoFire.query({ center: [1, 2], radius: undefined })).to.throw(); + // @ts-ignore expect(() => geoFire.query({ center: [1, 2], radius: 1000, other: 'throw' })).to.throw(); }); }); describe('updateCriteria():', () => { it('updateCriteria() updates query criteria', () => { - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - expect(geoQueries[0].center()).to.deep.equal([1, 2]); - expect(geoQueries[0].radius()).to.equal(1000); + expect(geoFireQueries[0].center()).to.deep.equal([1, 2]); + expect(geoFireQueries[0].radius()).to.equal(1000); - geoQueries[0].updateCriteria({ center: [2, 3], radius: 100 }); + geoFireQueries[0].updateCriteria({ center: [2, 3], radius: 100 }); - expect(geoQueries[0].center()).to.deep.equal([2, 3]); - expect(geoQueries[0].radius()).to.equal(100); + expect(geoFireQueries[0].center()).to.deep.equal([2, 3]); + expect(geoFireQueries[0].radius()).to.equal(100); }); it('updateCriteria() updates query criteria when given only center', () => { - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - expect(geoQueries[0].center()).to.deep.equal([1, 2]); - expect(geoQueries[0].radius()).to.equal(1000); + expect(geoFireQueries[0].center()).to.deep.equal([1, 2]); + expect(geoFireQueries[0].radius()).to.equal(1000); - geoQueries[0].updateCriteria({ center: [2, 3] }); + geoFireQueries[0].updateCriteria({ center: [2, 3] }); - expect(geoQueries[0].center()).to.deep.equal([2, 3]); - expect(geoQueries[0].radius()).to.equal(1000); + expect(geoFireQueries[0].center()).to.deep.equal([2, 3]); + expect(geoFireQueries[0].radius()).to.equal(1000); }); it('updateCriteria() updates query criteria when given only radius', () => { - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - expect(geoQueries[0].center()).to.deep.equal([1, 2]); - expect(geoQueries[0].radius()).to.equal(1000); + expect(geoFireQueries[0].center()).to.deep.equal([1, 2]); + expect(geoFireQueries[0].radius()).to.equal(1000); - geoQueries[0].updateCriteria({ radius: 100 }); + geoFireQueries[0].updateCriteria({ radius: 100 }); - expect(geoQueries[0].center()).to.deep.equal([1, 2]); - expect(geoQueries[0].radius()).to.equal(100); + expect(geoFireQueries[0].center()).to.deep.equal([1, 2]); + expect(geoFireQueries[0].radius()).to.equal(100); }); it('updateCriteria() fires \'key_entered\' callback for locations which now belong to the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); - geoQueries.push(geoFire.query({ center: [90, 90], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries.push(geoFire.query({ center: [90, 90], radius: 1000 })); + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -101,7 +108,7 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoQueries[0].updateCriteria({ center: [1, 2], radius: 1000 }); + geoFireQueries[0].updateCriteria({ center: [1, 2], radius: 1000 }); return wait(100); }).then(() => { @@ -112,8 +119,8 @@ describe('GeoFireQuery Tests:', () => { it('updateCriteria() fires \'key_entered\' callback for locations with complex keys which now belong to the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'loc:^:*1 entered', 'loc-+-+-4 entered'], expect, done); - geoQueries.push(geoFire.query({ center: [90, 90], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries.push(geoFire.query({ center: [90, 90], radius: 1000 })); + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -126,7 +133,7 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoQueries[0].updateCriteria({ center: [1, 2], radius: 1000 }); + geoFireQueries[0].updateCriteria({ center: [1, 2], radius: 1000 }); return wait(100); }).then(() => { @@ -137,8 +144,8 @@ describe('GeoFireQuery Tests:', () => { it('updateCriteria() fires \'key_exited\' callback for locations which no longer belong to the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 exited', 'loc4 exited'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -151,7 +158,7 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); + geoFireQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); return wait(100); }).then(() => { @@ -162,11 +169,11 @@ describe('GeoFireQuery Tests:', () => { it('updateCriteria() does not cause event callbacks to fire on the previous criteria', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc1 exited', 'loc4 exited', 'loc4 entered', 'loc5 entered'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -179,7 +186,7 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); + geoFireQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); return wait(100); }).then(() => { @@ -201,14 +208,14 @@ describe('GeoFireQuery Tests:', () => { it('updateCriteria() does not cause \'key_moved\' callbacks to fire for keys in both the previous and updated queries', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc4 exited', 'loc2 entered'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -221,7 +228,7 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoQueries[0].updateCriteria({ center: [1, 1], radius: 1000 }); + geoFireQueries[0].updateCriteria({ center: [1, 1], radius: 1000 }); return wait(100); }).then(() => { @@ -243,14 +250,14 @@ describe('GeoFireQuery Tests:', () => { it('updateCriteria() does not cause \'key_exited\' callbacks to fire twice for keys in the previous query but not in the updated query and which were moved after the query was updated', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'loc1 entered', 'loc4 entered', 'loc1 exited', 'loc4 exited', 'loc4 entered', 'loc5 entered', 'loc5 moved'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -264,7 +271,7 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); + geoFireQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); return wait(100); }).then(() => { @@ -295,38 +302,40 @@ describe('GeoFireQuery Tests:', () => { }); it('updateCriteria() does not throw errors given valid query criteria', () => { - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); validQueryCriterias.forEach((validQueryCriteria) => { - expect(() => geoQueries[0].updateCriteria(validQueryCriteria)).not.to.throw(); + expect(() => geoFireQueries[0].updateCriteria(validQueryCriteria)).not.to.throw(); }); }); it('updateCriteria() throws errors given invalid query criteria', () => { - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); invalidQueryCriterias.forEach((invalidQueryCriteria) => { - expect(() => geoQueries[0].updateCriteria(invalidQueryCriteria)).to.throw(); + // @ts-ignore + expect(() => geoFireQueries[0].updateCriteria(invalidQueryCriteria)).to.throw(); }); }); }); describe('on():', () => { it('on() throws error given invalid event type', () => { - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); const setInvalidEventType = () => { - geoQueries[0].on('invalid_event', () => { }); + geoFireQueries[0].on('invalid_event', () => { }); } expect(setInvalidEventType).to.throw(); }); it('on() throws error given invalid callback', () => { - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); const setInvalidCallback = () => { - geoQueries[0].on('key_entered', 'non-function'); + // @ts-ignore + geoFireQueries[0].on('key_entered', 'non-function'); } expect(setInvalidCallback).to.throw(); @@ -351,13 +360,13 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('ready', () => { + geoFireQueries[0].on('ready', () => { expect(cl.length()).to.be.equal(1); cl.x('ready fired'); }); @@ -381,16 +390,16 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('ready', () => { + geoFireQueries[0].on('ready', () => { expect(cl.length()).to.be.equal(2); cl.x('ready1 fired'); - geoQueries[0].on('ready', () => { + geoFireQueries[0].on('ready', () => { expect(cl.length()).to.be.equal(1); cl.x('ready2 fired'); }); @@ -400,14 +409,14 @@ describe('GeoFireQuery Tests:', () => { it('\'ready\' event fires after increasing the query radius, even if no new geohashes were queried', (done) => { const cl = new Checklist(['ready1 fired', 'ready2 fired'], expect, done); - geoQueries.push(geoFire.query({ center: [37.7851382, -122.405893], radius: 6 })); - const onReadyCallbackRegistration1 = geoQueries[0].on('ready', () => { + geoFireQueries.push(geoFire.query({ center: [37.7851382, -122.405893], radius: 6 })); + const onReadyCallbackRegistration1 = geoFireQueries[0].on('ready', () => { cl.x('ready1 fired'); onReadyCallbackRegistration1.cancel(); - geoQueries[0].updateCriteria({ + geoFireQueries[0].updateCriteria({ radius: 7 }); - geoQueries[0].on('ready', () => { + geoFireQueries[0].on('ready', () => { cl.x('ready2 fired'); }); }); @@ -425,27 +434,27 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - const onReadyCallbackRegistration1 = geoQueries[0].on('ready', () => { + const onReadyCallbackRegistration1 = geoFireQueries[0].on('ready', () => { expect(cl.length()).to.be.equal(6); cl.x('ready1 fired'); onReadyCallbackRegistration1.cancel(); - geoQueries[0].updateCriteria({ + geoFireQueries[0].updateCriteria({ center: [51, 51] }); - geoQueries[0].on('ready', () => { + geoFireQueries[0].on('ready', () => { expect(cl.length()).to.be.equal(1); cl.x('ready2 fired'); }); @@ -458,9 +467,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_moved\' callback does not fire for brand new locations within or outside of the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -480,9 +489,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_moved\' callback does not fire for locations outside of the GeoFireQuery which are moved somewhere else outside of the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -509,9 +518,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_moved\' callback does not fire for locations outside of the GeoFireQuery which are moved within the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -538,9 +547,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_moved\' callback does not fire for locations within the GeoFireQuery which are moved somewhere outside of the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -567,9 +576,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_moved\' callback does not fires for a location within the GeoFireQuery which is set to the same location', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc3 moved'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -597,9 +606,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_moved\' callback fires for locations within the GeoFireQuery which are moved somewhere else within the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -626,9 +635,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_moved\' callback gets passed correct location parameter', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved to 2,2', 'loc3 moved to -1,-1'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved to ' + location); }); @@ -655,9 +664,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_moved\' callback gets passed correct distance parameter', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved (111.19 km from center)', 'loc3 moved (400.90 km from center)'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved (' + distance.toFixed(2) + ' km from center)'); }); @@ -684,9 +693,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_moved\' callback properly fires when multiple keys are at the same location within the GeoFireQuery and only one of them moves somewhere else within the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -713,9 +722,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_moved\' callback properly fires when a location within the GeoFireQuery moves somehwere else within the GeoFireQuery that is already occupied by another key', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -742,12 +751,12 @@ describe('GeoFireQuery Tests:', () => { it('multiple \'key_moved\' callbacks fire for locations within the GeoFireQuery which are moved somewhere else within the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved1', 'loc3 moved1', 'loc1 moved2', 'loc3 moved2'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved1'); }); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved2'); }); @@ -776,7 +785,7 @@ describe('GeoFireQuery Tests:', () => { it('\'key_entered\' callback fires when a location enters the GeoFireQuery before onKeyEntered() was called', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); geoFire.set({ 'loc1': [2, 3], @@ -787,7 +796,7 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -800,9 +809,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_entered\' callback fires when a location enters the GeoFireQuery after onKeyEntered() was called', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -824,9 +833,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_entered\' callback gets passed correct location parameter', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 entered at 2,3', 'loc4 entered at 5,5'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered at ' + location); }); @@ -848,9 +857,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_entered\' callback gets passed correct distance parameter', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 entered (157.23 km from center)', 'loc4 entered (555.66 km from center)'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered (' + distance.toFixed(2) + ' km from center)'); }); @@ -872,9 +881,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_entered\' callback properly fires when multiple keys are at the same location outside the GeoFireQuery and only one of them moves within the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 entered'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -898,9 +907,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_entered\' callback properly fires when a location outside the GeoFireQuery moves somewhere within the GeoFireQuery that is already occupied by another key', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 entered', 'loc3 entered'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -924,12 +933,12 @@ describe('GeoFireQuery Tests:', () => { it('multiple \'key_entered\' callbacks fire when a location enters the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 entered1', 'loc4 entered1', 'loc1 entered2', 'loc4 entered2'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered1'); }); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered2'); }); @@ -953,9 +962,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_exited\' callback fires when a location leaves the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited', 'loc4 exited'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -984,9 +993,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_exited\' callback gets passed correct location parameter', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited to 25,90', 'loc4 exited to 25,5'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited to ' + location); }); @@ -1016,9 +1025,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_exited\' callback gets passed correct distance parameter', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited (9759.01 km from center)', 'loc4 exited (2688.06 km from center)'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited (' + distance.toFixed(2) + ' km from center)'); }); @@ -1048,9 +1057,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_exited\' callback gets passed null for location and distance parameters if the key is entirely removed from GeoFire', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { expect(location).to.be.equal(null); expect(distance).to.be.equal(null); cl.x(key + ' exited'); @@ -1072,9 +1081,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_exited\' callback fires when a location within the GeoFireQuery is entirely removed from GeoFire', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -1096,9 +1105,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_exited\' callback properly fires when multiple keys are at the same location inside the GeoFireQuery and only one of them moves outside the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -1122,9 +1131,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_exited\' callback properly fires when a location inside the GeoFireQuery moves somewhere outside the GeoFireQuery that is already occupied by another key', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -1148,12 +1157,12 @@ describe('GeoFireQuery Tests:', () => { it('multiple \'key_exited\' callbacks fire when a location leaves the GeoFireQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited1', 'loc4 exited1', 'loc1 exited2', 'loc4 exited2'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited1'); }); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited2'); }); @@ -1184,15 +1193,15 @@ describe('GeoFireQuery Tests:', () => { it('\'key_*\' event callbacks fire when used all at the same time', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc1 moved', 'loc4 exited', 'loc1 exited', 'loc5 entered'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -1229,15 +1238,15 @@ describe('GeoFireQuery Tests:', () => { it('location moving between geohash queries triggers a key_moved', (done) => { const cl = new Checklist(['loc1 entered', 'loc2 entered', 'p1', 'loc1 moved', 'loc2 moved', 'p2'], expect, done); - geoQueries.push(geoFire.query({ center: [0, 0], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [0, 0], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -1263,15 +1272,15 @@ describe('GeoFireQuery Tests:', () => { it('cancel() prevents GeoFireQuery from firing any more \'key_*\' event callbacks', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 entered', 'loc4 entered', 'loc1 moved', 'loc4 exited'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -1295,17 +1304,17 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p3') - geoQueries[0].cancel(); + geoFireQueries[0].cancel(); return wait(1000); }).then(() => { - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -1323,29 +1332,29 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('Calling cancel() on one GeoFireQuery does not cancel other GeoQueries', (done) => { + it('Calling cancel() on one GeoFireQuery does not cancel other geoFireQueries', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 entered1', 'loc1 entered2', 'loc4 entered1', 'loc4 entered2', 'loc1 moved1', 'loc1 moved2', 'loc4 exited1', 'loc4 exited2', 'loc1 exited2', 'loc5 entered2'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered1'); }); - geoQueries[0].on('key_exited', (key, location, distance) => { + geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited1'); }); - geoQueries[0].on('key_moved', (key, location, distance) => { + geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved1'); }); - geoQueries[1].on('key_entered', (key, location, distance) => { + geoFireQueries[1].on('key_entered', (key, location, distance) => { cl.x(key + ' entered2'); }); - geoQueries[1].on('key_exited', (key, location, distance) => { + geoFireQueries[1].on('key_exited', (key, location, distance) => { cl.x(key + ' exited2'); }); - geoQueries[1].on('key_moved', (key, location, distance) => { + geoFireQueries[1].on('key_moved', (key, location, distance) => { cl.x(key + ' moved2'); }); @@ -1369,7 +1378,7 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p3') - geoQueries[0].cancel(); + geoFireQueries[0].cancel(); return geoFire.set({ 'loc1': [10, -100], @@ -1388,7 +1397,7 @@ describe('GeoFireQuery Tests:', () => { it('Calling cancel() in the middle of firing \'key_entered\' events is allowed', (done) => { const cl = new Checklist(['p1', 'key entered', 'cancel query'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); geoFire.set({ 'loc1': [1, 2], @@ -1398,12 +1407,12 @@ describe('GeoFireQuery Tests:', () => { cl.x('p1'); let numKeyEnteredEventsFired = 0; - geoQueries[0].on('key_entered', (key, location, distance) => { + geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x('key entered'); numKeyEnteredEventsFired++; if (numKeyEnteredEventsFired === 1) { cl.x('cancel query'); - geoQueries[0].cancel(); + geoFireQueries[0].cancel(); } }); }).catch(failTestOnCaughtError); diff --git a/tests/geoFirestore/index.test.ts b/tests/geoFirestore/index.test.ts new file mode 100755 index 00000000..c6c75a71 --- /dev/null +++ b/tests/geoFirestore/index.test.ts @@ -0,0 +1,851 @@ +import * as chai from 'chai'; + +import { GeoFirestore } from '../../src/geoFirestore'; +import { GeoFirestoreQuery } from '../../src/geoFirestore/query'; +import { + afterEachHelperFirestore, beforeEachHelperFirestore, Checklist, failTestOnCaughtError, geoFirestore, geoFirestoreRef, getFirestoreData, geoFirestoreQueries, + invalidFirebaseRefs, invalidKeys, invalidLocations, invalidQueryCriterias, validKeys, validLocations, validQueryCriterias +} from '../common'; + +const expect = chai.expect; + +describe('GeoFirestore Tests:', () => { + // Reset the Firestore before each test + beforeEach((done) => { + beforeEachHelperFirestore(done); + }); + + afterEach((done) => { + afterEachHelperFirestore(done); + }); + + describe('Constructor:', () => { + it('Constructor throws errors given invalid Firebase references', () => { + invalidFirebaseRefs.forEach((invalidFirebaseRef) => { + // @ts-ignore + expect(() => new GeoFirestore(invalidFirebaseRef)).to.throw(null, 'collectionRef must be an instance of a Firestore Collection'); + }); + }); + + it('Constructor does not throw errors given valid Firebase references', () => { + expect(() => new GeoFirestore(geoFirestoreRef)).not.to.throw(); + }); + }); + + describe('ref():', () => { + it('ref() returns the Firebase reference used to create a GeoFire instance', () => { + expect(geoFirestore.ref()).to.deep.equal(geoFirestoreRef); + }); + }); + + describe('Adding a single location via set():', () => { + it('set() returns a promise', (done) => { + + const cl = new Checklist(['p1'], expect, done); + + geoFirestore.set('loc1', [0, 0]).then(() => { + cl.x('p1'); + }); + }); + + it('set() updates Firebase when adding new locations', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); + + geoFirestore.set('loc1', [0, 0]).then(() => { + cl.x('p1'); + + return geoFirestore.set('loc2', [50, 50]); + }).then(() => { + cl.x('p2'); + + return geoFirestore.set('loc3', [-90, -90]); + }).then(() => { + cl.x('p3'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [ 50, 50 ], 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': [ -90, -90 ], 'g': '1bpbpbpbpb' } + }); + + cl.x('p4'); + }).catch((error) => { + console.log(error); + failTestOnCaughtError(error) + }); + }); + + it('set() handles decimal latitudes and longitudes', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); + + geoFirestore.set('loc1', [0.254, 0]).then(() => { + cl.x('p1'); + + return geoFirestore.set('loc2', [50, 50.293403]); + }).then(() => { + cl.x('p2'); + + return geoFirestore.set('loc3', [-82.614, -90.938]); + }).then(() => { + cl.x('p3'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': 'ebpcrypzxv', 'l': [ 0.254, 0 ], 'g': 'ebpcrypzxv' }, + 'loc2': { '.priority': 'v0gu2qnx15', 'l': [ 50, 50.293403 ], 'g': 'v0gu2qnx15' }, + 'loc3': { '.priority': '1cr648sfx4', 'l': [ -82.614, -90.938 ], 'g': '1cr648sfx4' } + }); + + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('set() updates Firebase when changing a pre-existing key', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + + geoFirestore.set('loc1', [0, 0]).then(() => { + cl.x('p1'); + + return geoFirestore.set('loc2', [50, 50]); + }).then(() => { + cl.x('p2'); + + return geoFirestore.set('loc3', [-90, -90]); + }).then(() => { + cl.x('p3'); + + return geoFirestore.set('loc1', [2, 3]); + }).then(() => { + cl.x('p4'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': 's065kk0dc5', 'l': [ 2, 3 ], 'g': 's065kk0dc5' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [ 50, 50 ], 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': [ -90, -90 ], 'g': '1bpbpbpbpb' } + }); + + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('set() updates Firebase when changing a pre-existing key to the same location', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + + geoFirestore.set('loc1', [0, 0]).then(() => { + cl.x('p1'); + + return geoFirestore.set('loc2', [50, 50]); + }).then(() => { + cl.x('p2'); + + return geoFirestore.set('loc3', [-90, -90]); + }).then(() => { + cl.x('p3'); + + return geoFirestore.set('loc1', [0, 0]); + }).then(() => { + cl.x('p4'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [ 50, 50 ], 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': [ -90, -90 ], 'g': '1bpbpbpbpb' } + }); + + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('set() handles multiple keys at the same location', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); + + geoFirestore.set('loc1', [0, 0]).then(() => { + cl.x('p1'); + + return geoFirestore.set('loc2', [0, 0]); + }).then(() => { + cl.x('p2'); + + return geoFirestore.set('loc3', [0, 0]); + }).then(() => { + cl.x('p3'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' }, + 'loc3': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' } + }); + + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('set() updates Firebase after complex operations', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9', 'p10', 'p11'], expect, done); + + geoFirestore.set('loc:1', [0, 0]).then(() => { + cl.x('p1'); + + return geoFirestore.set('loc2', [50, 50]); + }).then(() => { + cl.x('p2'); + + return geoFirestore.set('loc%!A72f()3', [-90, -90]); + }).then(() => { + cl.x('p3'); + + return geoFirestore.remove('loc2'); + }).then(() => { + cl.x('p4'); + + return geoFirestore.set('loc2', [0.2358, -72.621]); + }).then(() => { + cl.x('p5'); + + return geoFirestore.set('loc4', [87.6, -130]); + }).then(() => { + cl.x('p6'); + + return geoFirestore.set('loc5', [5, 55.555]); + }).then(() => { + cl.x('p7'); + + return geoFirestore.set('loc5', null); + }).then(() => { + cl.x('p8'); + + return geoFirestore.set('loc:1', [87.6, -130]); + }).then(() => { + cl.x('p9'); + + return geoFirestore.set('loc6', [-72.258, 0.953215]); + }).then(() => { + cl.x('p10'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc:1': { '.priority': 'cped3g0fur', 'l': [ 87.6, -130 ], 'g': 'cped3g0fur' }, + 'loc2': { '.priority': 'd2h376zj8h', 'l': [ 0.2358, -72.621 ], 'g': 'd2h376zj8h' }, + 'loc%!A72f()3': { '.priority': '1bpbpbpbpb', 'l': [ -90, -90 ], 'g': '1bpbpbpbpb' }, + 'loc4': { '.priority': 'cped3g0fur', 'l': [ 87.6, -130 ], 'g': 'cped3g0fur' }, + 'loc6': { '.priority': 'h50svty4es', 'l': [ -72.258, 0.953215 ], 'g': 'h50svty4es' } + }); + + cl.x('p11'); + }).catch(failTestOnCaughtError); + }); + + it('set() does not throw errors given valid keys', () => { + validKeys.forEach((validKey) => { + expect(() => { + geoFirestore.set(validKey, [0, 0]); + }).not.to.throw(); + }); + }); + + it('set() throws errors given invalid keys', () => { + invalidKeys.forEach((invalidKey) => { + expect(() => { + geoFirestore.set(invalidKey, [0, 0]); + }).to.throw(); + }); + }); + + it('set() does not throw errors given valid locations', () => { + validLocations.forEach((validLocation, i) => { + expect(() => { + geoFirestore.set('loc', validLocation); + }).not.to.throw(); + }); + }); + + it('set() throws errors given invalid locations', () => { + invalidLocations.forEach((invalidLocation, i) => { + // Setting location to null is valid since it will remove the key + if (invalidLocation !== null) { + expect(() => { + // @ts-ignore + geoFirestore.set('loc', invalidLocation); + }).to.throw(); + } + }); + }); + }); + + describe('Adding multiple locations via set():', () => { + it('set() returns a promise', (done) => { + + const cl = new Checklist(['p1'], expect, done); + + geoFirestore.set({ + 'loc1': [0, 0] + }).then(() => { + cl.x('p1'); + }); + }); + + it('set() updates Firebase when adding new locations', (done) => { + const cl = new Checklist(['p1', 'p2'], expect, done); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, 50], + 'loc3': [-90, -90] + }).then(() => { + cl.x('p1'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [ 50, 50 ], 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': [ -90, -90 ], 'g': '1bpbpbpbpb' } + }); + + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('set() handles decimal latitudes and longitudes', (done) => { + const cl = new Checklist(['p1', 'p2'], expect, done); + + geoFirestore.set({ + 'loc1': [0.254, 0], + 'loc2': [50, 50.293403], + 'loc3': [-82.614, -90.938] + }).then(() => { + cl.x('p1'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': 'ebpcrypzxv', 'l': [ 0.254, 0 ], 'g': 'ebpcrypzxv' }, + 'loc2': { '.priority': 'v0gu2qnx15', 'l': [ 50, 50.293403 ], 'g': 'v0gu2qnx15' }, + 'loc3': { '.priority': '1cr648sfx4', 'l': [ -82.614, -90.938 ], 'g': '1cr648sfx4' } + }); + + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('set() updates Firebase when changing a pre-existing key', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, 50], + 'loc3': [-90, -90] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [2, 3] + }); + }).then(() => { + cl.x('p2'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': 's065kk0dc5', 'l': [ 2, 3 ], 'g': 's065kk0dc5' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [ 50, 50 ], 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': [ -90, -90 ], 'g': '1bpbpbpbpb' } + }); + + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('set() updates Firebase when changing a pre-existing key to the same location', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, 50], + 'loc3': [-90, -90] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [0, 0] + }); + }).then(() => { + cl.x('p2'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [ 50, 50 ], 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': [ -90, -90 ], 'g': '1bpbpbpbpb' } + }); + + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('set() handles multiple keys at the same location', (done) => { + const cl = new Checklist(['p1', 'p2'], expect, done); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [0, 0], + 'loc3': [0, 0] + }).then(() => { + cl.x('p1'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' }, + 'loc3': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' } + }); + + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('set() updates Firebase after complex operations', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6'], expect, done); + + geoFirestore.set({ + 'loc:1': [0, 0], + 'loc2': [50, 50], + 'loc%!A72f()3': [-90, -90] + }).then(() => { + cl.x('p1'); + + return geoFirestore.remove('loc2'); + }).then(() => { + cl.x('p2'); + + return geoFirestore.set({ + 'loc2': [0.2358, -72.621], + 'loc4': [87.6, -130], + 'loc5': [5, 55.555] + }); + }).then(() => { + cl.x('p3'); + + return geoFirestore.set({ + 'loc5': null + }); + }).then(() => { + cl.x('p4'); + + return geoFirestore.set({ + 'loc:1': [87.6, -130], + 'loc6': [-72.258, 0.953215] + }); + }).then(() => { + cl.x('p5'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc:1': { '.priority': 'cped3g0fur', 'l': [ 87.6, -130 ], 'g': 'cped3g0fur' }, + 'loc2': { '.priority': 'd2h376zj8h', 'l': [ 0.2358, -72.621 ], 'g': 'd2h376zj8h' }, + 'loc%!A72f()3': { '.priority': '1bpbpbpbpb', 'l': [ -90, -90 ], 'g': '1bpbpbpbpb' }, + 'loc4': { '.priority': 'cped3g0fur', 'l': [ 87.6, -130 ], 'g': 'cped3g0fur' }, + 'loc6': { '.priority': 'h50svty4es', 'l': [ -72.258, 0.953215 ], 'g': 'h50svty4es' } + }); + + cl.x('p6'); + }).catch(failTestOnCaughtError); + }); + + it('set() does not throw errors given valid keys', () => { + validKeys.forEach((validKey) => { + expect(() => { + const locations = {}; + locations[validKey] = [0, 0]; + geoFirestore.set(locations); + }).not.to.throw(); + }); + }); + + it('set() throws errors given invalid keys', () => { + invalidKeys.forEach((invalidKey) => { + if (invalidKey !== null && invalidKey !== undefined && typeof invalidKey !== 'boolean') { + expect(() => { + const locations = {}; + // @ts-ignore + locations[invalidKey] = [0, 0]; + geoFirestore.set(locations); + }).to.throw(); + } + }); + }); + + it('set() throws errors given a location argument in combination with an object', () => { + expect(() => { + geoFirestore.set({ + 'loc': [0, 0] + }, [0, 0]); + }).to.throw(); + }); + + it('set() does not throw errors given valid locations', () => { + validLocations.forEach((validLocation, i) => { + expect(() => { + geoFirestore.set({ + 'loc': validLocation + }); + }).not.to.throw(); + }); + }); + + it('set() throws errors given invalid locations', () => { + invalidLocations.forEach((invalidLocation, i) => { + // Setting location to null is valid since it will remove the key + if (invalidLocation !== null) { + expect(() => { + geoFirestore.set({ + 'loc': invalidLocation + }); + }).to.throw(); + } + }); + }); + }); + + describe('Retrieving locations:', () => { + it('get() returns a promise', (done) => { + const cl = new Checklist(['p1'], expect, done); + + geoFirestore.get('loc1').then((ref) => { + console.log(ref) + cl.x('p1'); + }); + }); + + it('get() returns null for non-existent keys', (done) => { + const cl = new Checklist(['p1'], expect, done); + + geoFirestore.get('loc1').then((location) => { + expect(location).to.equal(null); + + cl.x('p1'); + }); + }); + + it('get() retrieves locations given existing keys', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, 50], + 'loc3': [-90, -90] + }).then(() => { + cl.x('p1'); + + return geoFirestore.get('loc1'); + }).then((location) => { + expect(location).to.deep.equal([0, 0]); + cl.x('p2'); + + return geoFirestore.get('loc2'); + }).then((location) => { + expect(location).to.deep.equal([50, 50]); + cl.x('p3'); + + return geoFirestore.get('loc3'); + }).then((location) => { + expect(location).to.deep.equal([-90, -90]); + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('get() does not throw errors given valid keys', () => { + validKeys.forEach((validKey) => { + expect(() => geoFirestore.get(validKey)).not.to.throw(); + }); + }); + + it('get() throws errors given invalid keys', () => { + invalidKeys.forEach((invalidKey) => { + // @ts-ignore + expect(() => geoFirestore.get(invalidKey)).to.throw(); + }); + }); + }); + + describe('Removing locations:', () => { + it('set() removes existing location given null', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [2, 3] + }).then(() => { + cl.x('p1'); + + return geoFirestore.get('loc1'); + }).then((location) => { + expect(location).to.deep.equal([0, 0]); + + cl.x('p2'); + + return geoFirestore.set('loc1', null); + }).then(() => { + cl.x('p3'); + + return geoFirestore.get('loc1'); + }).then((location) => { + console.log('location', location); + expect(location).to.equal(null); + + cl.x('p4'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc2': { '.priority': 's065kk0dc5', 'l': [ 2, 3 ], 'g': 's065kk0dc5' } + }); + + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('set() does nothing given a non-existent location and null', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + + geoFirestore.set('loc1', [0, 0]).then(() => { + cl.x('p1'); + + return geoFirestore.get('loc1'); + }).then((location) => { + expect(location).to.deep.equal([0, 0]); + + cl.x('p2'); + + return geoFirestore.set('loc2', null); + }).then(() => { + cl.x('p3'); + + return geoFirestore.get('loc2'); + }).then((location) => { + expect(location).to.equal(null); + + cl.x('p4'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' } + }); + + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('set() removes existing location given null', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [2, 3] + }).then(() => { + cl.x('p1'); + + return geoFirestore.get('loc1'); + }).then((location) => { + expect(location).to.deep.equal([0, 0]); + + cl.x('p2'); + + return geoFirestore.set({ + 'loc1': null, + 'loc3': [-90, -90] + }); + }).then(() => { + cl.x('p3'); + + return geoFirestore.get('loc1'); + }).then((location) => { + expect(location).to.equal(null); + + cl.x('p4'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc2': { '.priority': 's065kk0dc5', 'l': [2, 3], 'g': 's065kk0dc5' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' } + }); + + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('set() does nothing given a non-existent location and null', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': null + }).then(() => { + cl.x('p1'); + + return geoFirestore.get('loc1'); + }).then((location) => { + expect(location).to.deep.equal([0, 0]); + + cl.x('p2'); + + return geoFirestore.get('loc2'); + }).then((location) => { + expect(location).to.equal(null); + + cl.x('p3'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' } + }); + + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('remove() removes existing location', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + + geoFirestore.set({ + 'loc:^%*1': [0, 0], + 'loc2': [2, 3] + }).then(() => { + cl.x('p1'); + + return geoFirestore.get('loc:^%*1'); + }).then((location) => { + expect(location).to.deep.equal([0, 0]); + + cl.x('p2'); + + return geoFirestore.remove('loc:^%*1'); + }).then(() => { + cl.x('p3'); + + return geoFirestore.get('loc:^%*1'); + }).then((location) => { + expect(location).to.equal(null); + + cl.x('p4'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc2': { '.priority': 's065kk0dc5', 'l': [ 2, 3 ], 'g': 's065kk0dc5' } + }); + + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('remove() does nothing given a non-existent location', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); + + geoFirestore.set('loc1', [0, 0]).then(() => { + cl.x('p1'); + + return geoFirestore.get('loc1'); + }).then((location) => { + expect(location).to.deep.equal([0, 0]); + + cl.x('p2'); + + return geoFirestore.remove('loc2'); + }).then(() => { + cl.x('p3'); + + return geoFirestore.get('loc2'); + }).then((location) => { + expect(location).to.equal(null); + + cl.x('p4'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' } + }); + + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('remove() only removes one key if multiple keys are at the same location', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [2, 3], + 'loc3': [0, 0] + }).then(() => { + cl.x('p1'); + + return geoFirestore.remove('loc1'); + }).then(() => { + cl.x('p2'); + + return getFirestoreData(); + }).then((firebaseData) => { + expect(firebaseData).to.deep.equal({ + 'loc2': { '.priority': 's065kk0dc5', 'l': [ 2, 3 ], 'g': 's065kk0dc5' }, + 'loc3': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' } + }); + + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('remove() does not throw errors given valid keys', () => { + validKeys.forEach((validKey) => { + expect(() => geoFirestore.remove(validKey)).not.to.throw(); + }); + }); + + it('remove() throws errors given invalid keys', () => { + invalidKeys.forEach((invalidKey) => { + // @ts-ignore + expect(() => geoFirestore.remove(invalidKey)).to.throw(); + }); + }); + }); + + describe('query():', () => { + it('query() returns GeoFireQuery instance', () => { + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + expect(geoFirestoreQueries[0] instanceof GeoFirestoreQuery).to.be.ok; + }); + + it('query() does not throw errors given valid query criteria', () => { + validQueryCriterias.forEach((validQueryCriteria) => { + if (typeof validQueryCriteria.center !== 'undefined' && typeof validQueryCriteria.radius !== 'undefined') { + expect(() => geoFirestore.query(validQueryCriteria)).not.to.throw(); + } + }); + }); + + it('query() throws errors given invalid query criteria', () => { + invalidQueryCriterias.forEach((invalidQueryCriteria) => { + // @ts-ignore + expect(() => geoFirestore.query(invalidQueryCriteria)).to.throw(); + }); + }); + }); +}); diff --git a/tests/geoFirestore/query.test.ts b/tests/geoFirestore/query.test.ts new file mode 100644 index 00000000..da720d95 --- /dev/null +++ b/tests/geoFirestore/query.test.ts @@ -0,0 +1,1421 @@ +import * as chai from 'chai'; + +import { + afterEachHelperFirestore, beforeEachHelperFirestore, Checklist, failTestOnCaughtError, geoFirestore, geoFirestoreQueries, invalidQueryCriterias, validQueryCriterias, wait +} from '../common'; + +const expect = chai.expect; + +describe('GeoFirestoreQuery Tests:', () => { + // Reset the Firestore before each test + beforeEach((done) => { + beforeEachHelperFirestore(done); + }); + + afterEach((done) => { + afterEachHelperFirestore(done); + }); + + describe('Constructor:', () => { + it('Constructor stores query criteria', () => { + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + expect(geoFirestoreQueries[0].center()).to.deep.equal([1, 2]); + expect(geoFirestoreQueries[0].radius()).to.equal(1000); + }); + + it('Constructor throws error on invalid query criteria', () => { + expect(() => geoFirestore.query({})).to.throw(); + // @ts-ignore + expect(() => geoFirestore.query({ random: 100 })).to.throw(); + expect(() => geoFirestore.query({ center: [1, 2] })).to.throw(); + expect(() => geoFirestore.query({ radius: 1000 })).to.throw(); + expect(() => geoFirestore.query({ center: [91, 2], radius: 1000 })).to.throw(); + expect(() => geoFirestore.query({ center: [1, -181], radius: 1000 })).to.throw(); + // @ts-ignore + expect(() => geoFirestore.query({ center: ['text', 2], radius: 1000 })).to.throw(); + // @ts-ignore + expect(() => geoFirestore.query({ center: [1, [1, 2]], radius: 1000 })).to.throw(); + // @ts-ignore + expect(() => geoFirestore.query({ center: 1000, radius: 1000 })).to.throw(); + expect(() => geoFirestore.query({ center: null, radius: 1000 })).to.throw(); + expect(() => geoFirestore.query({ center: undefined, radius: 1000 })).to.throw(); + expect(() => geoFirestore.query({ center: [null, 2], radius: 1000 })).to.throw(); + expect(() => geoFirestore.query({ center: [1, undefined], radius: 1000 })).to.throw(); + expect(() => geoFirestore.query({ center: [1, 2], radius: -10 })).to.throw(); + // @ts-ignore + expect(() => geoFirestore.query({ center: [1, 2], radius: 'text' })).to.throw(); + // @ts-ignore + expect(() => geoFirestore.query({ center: [1, 2], radius: [1, 2] })).to.throw(); + expect(() => geoFirestore.query({ center: [1, 2], radius: null })).to.throw(); + expect(() => geoFirestore.query({ center: [1, 2], radius: undefined })).to.throw(); + // @ts-ignore + expect(() => geoFirestore.query({ center: [1, 2], radius: 1000, other: 'throw' })).to.throw(); + }); + }); + + describe('updateCriteria():', () => { + it('updateCriteria() updates query criteria', () => { + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + expect(geoFirestoreQueries[0].center()).to.deep.equal([1, 2]); + expect(geoFirestoreQueries[0].radius()).to.equal(1000); + + geoFirestoreQueries[0].updateCriteria({ center: [2, 3], radius: 100 }); + + expect(geoFirestoreQueries[0].center()).to.deep.equal([2, 3]); + expect(geoFirestoreQueries[0].radius()).to.equal(100); + }); + + it('updateCriteria() updates query criteria when given only center', () => { + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + expect(geoFirestoreQueries[0].center()).to.deep.equal([1, 2]); + expect(geoFirestoreQueries[0].radius()).to.equal(1000); + + geoFirestoreQueries[0].updateCriteria({ center: [2, 3] }); + + expect(geoFirestoreQueries[0].center()).to.deep.equal([2, 3]); + expect(geoFirestoreQueries[0].radius()).to.equal(1000); + }); + + it('updateCriteria() updates query criteria when given only radius', () => { + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + expect(geoFirestoreQueries[0].center()).to.deep.equal([1, 2]); + expect(geoFirestoreQueries[0].radius()).to.equal(1000); + + geoFirestoreQueries[0].updateCriteria({ radius: 100 }); + + expect(geoFirestoreQueries[0].center()).to.deep.equal([1, 2]); + expect(geoFirestoreQueries[0].radius()).to.equal(100); + }); + + it('updateCriteria() fires \'key_entered\' callback for locations which now belong to the GeoFirestoreQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [90, 90], radius: 1000 })); + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + geoFirestoreQueries[0].updateCriteria({ center: [1, 2], radius: 1000 }); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('updateCriteria() fires \'key_entered\' callback for locations with complex keys which now belong to the GeoFirestoreQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'loc:^:*1 entered', 'loc-+-+-4 entered'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [90, 90], radius: 1000 })); + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + + geoFirestore.set({ + 'loc:^:*1': [2, 3], + 'loc:a:a:a:a:2': [50, -7], + 'loc%!@3': [16, -150], + 'loc-+-+-4': [5, 5], + 'loc:5': [67, 55] + }).then(() => { + cl.x('p1'); + + geoFirestoreQueries[0].updateCriteria({ center: [1, 2], radius: 1000 }); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('updateCriteria() fires \'key_exited\' callback for locations which no longer belong to the GeoFirestoreQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'loc1 exited', 'loc4 exited'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited'); + }); + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + geoFirestoreQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('updateCriteria() does not cause event callbacks to fire on the previous criteria', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc1 exited', 'loc4 exited', 'loc4 entered', 'loc5 entered'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited'); + }); + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [88, 88] + }).then(() => { + cl.x('p1'); + + geoFirestoreQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); + + return wait(100); + }).then(() => { + cl.x('p2'); + + return geoFirestore.set({ + 'loc2': [1, 1], + 'loc4': [89, 90] + }); + }).then(() => { + cl.x('p3'); + + return wait(100); + }).then(() => { + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('updateCriteria() does not cause \'key_moved\' callbacks to fire for keys in both the previous and updated queries', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc4 exited', 'loc2 entered'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited'); + }); + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved'); + }); + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [88, 88] + }).then(() => { + cl.x('p1'); + + geoFirestoreQueries[0].updateCriteria({ center: [1, 1], radius: 1000 }); + + return wait(100); + }).then(() => { + cl.x('p2'); + + return geoFirestore.set({ + 'loc2': [1, 1], + 'loc4': [89, 90] + }); + }).then(() => { + cl.x('p3'); + + return wait(100); + }).then(() => { + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('updateCriteria() does not cause \'key_exited\' callbacks to fire twice for keys in the previous query but not in the updated query and which were moved after the query was updated', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'loc1 entered', 'loc4 entered', 'loc1 exited', 'loc4 exited', 'loc4 entered', 'loc5 entered', 'loc5 moved'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited'); + }); + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved'); + }); + + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [88, 88] + }).then(() => { + cl.x('p1'); + + geoFirestoreQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); + + return wait(100); + }).then(() => { + cl.x('p2'); + + return geoFirestore.set({ + 'loc2': [1, 1], + 'loc4': [89, 90] + }); + }).then(() => { + cl.x('p3'); + + return wait(100); + }).then(() => { + cl.x('p4'); + + return geoFirestore.set({ + 'loc2': [0, 0], + 'loc5': [89, 89] + }); + }).then(() => { + cl.x('p5'); + + return wait(100); + }).then(() => { + cl.x('p6'); + }).catch(failTestOnCaughtError); + }); + + it('updateCriteria() does not throw errors given valid query criteria', () => { + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + validQueryCriterias.forEach((validQueryCriteria) => { + expect(() => geoFirestoreQueries[0].updateCriteria(validQueryCriteria)).not.to.throw(); + }); + }); + + it('updateCriteria() throws errors given invalid query criteria', () => { + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + invalidQueryCriterias.forEach((invalidQueryCriteria) => { + // @ts-ignore + expect(() => geoFirestoreQueries[0].updateCriteria(invalidQueryCriteria)).to.throw(); + }); + }); + }); + + describe('on():', () => { + it('on() throws error given invalid event type', () => { + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + const setInvalidEventType = () => { + geoFirestoreQueries[0].on('invalid_event', () => { }); + } + + expect(setInvalidEventType).to.throw(); + }); + + it('on() throws error given invalid callback', () => { + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + const setInvalidCallback = () => { + // @ts-ignore + geoFirestoreQueries[0].on('key_entered', 'non-function'); + } + + expect(setInvalidCallback).to.throw(); + }); + }); + + describe('\'ready\' event:', () => { + it('\'ready\' event fires after all \'key_entered\' events have fired', (done) => { + const cl = new Checklist(['p1', 'loc1 entered', 'loc2 entered', 'loc5 entered', 'loc6 entered', 'loc7 entered', 'loc10 entered', 'ready fired'], expect, done); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [1, 1], + 'loc3': [50, 50], + 'loc4': [14, 1], + 'loc5': [1, 2], + 'loc6': [1, 1], + 'loc7': [0, 0], + 'loc8': [-80, 44], + 'loc9': [1, -136], + 'loc10': [-2, -2] + }).then(() => { + cl.x('p1'); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + + geoFirestoreQueries[0].on('ready', () => { + expect(cl.length()).to.be.equal(1); + cl.x('ready fired'); + }); + }); + }); + + it('\'ready\' event fires immediately if the callback is added after the query is already ready', (done) => { + const cl = new Checklist(['p1', 'loc1 entered', 'loc2 entered', 'loc5 entered', 'loc6 entered', 'loc7 entered', 'loc10 entered', 'ready1 fired', 'ready2 fired'], expect, done); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [1, 1], + 'loc3': [50, 50], + 'loc4': [14, 1], + 'loc5': [1, 2], + 'loc6': [1, 1], + 'loc7': [0, 0], + 'loc8': [-80, 44], + 'loc9': [1, -136], + 'loc10': [-2, -2] + }).then(() => { + cl.x('p1'); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + + geoFirestoreQueries[0].on('ready', () => { + expect(cl.length()).to.be.equal(2); + cl.x('ready1 fired'); + geoFirestoreQueries[0].on('ready', () => { + expect(cl.length()).to.be.equal(1); + cl.x('ready2 fired'); + }); + }); + }); + }); + + it('\'ready\' event fires after increasing the query radius, even if no new geohashes were queried', (done) => { + const cl = new Checklist(['ready1 fired', 'ready2 fired'], expect, done); + geoFirestoreQueries.push(geoFirestore.query({ center: [37.7851382, -122.405893], radius: 6 })); + const onReadyCallbackRegistration1 = geoFirestoreQueries[0].on('ready', () => { + cl.x('ready1 fired'); + onReadyCallbackRegistration1.cancel(); + geoFirestoreQueries[0].updateCriteria({ + radius: 7 + }); + geoFirestoreQueries[0].on('ready', () => { + cl.x('ready2 fired'); + }); + }); + }); + + it('updateCriteria() fires the \'ready\' event after all \'key_entered\' events have fired', (done) => { + const cl = new Checklist(['p1', 'loc1 entered', 'loc2 entered', 'loc5 entered', 'loc3 entered', 'loc1 exited', 'loc2 exited', 'loc5 exited', 'ready1 fired', 'ready2 fired'], expect, done); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [1, 1], + 'loc3': [50, 50], + 'loc4': [14, 1], + 'loc5': [1, 2] + }).then(() => { + cl.x('p1'); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited'); + }); + + const onReadyCallbackRegistration1 = geoFirestoreQueries[0].on('ready', () => { + expect(cl.length()).to.be.equal(6); + cl.x('ready1 fired'); + + onReadyCallbackRegistration1.cancel(); + + geoFirestoreQueries[0].updateCriteria({ + center: [51, 51] + }); + + geoFirestoreQueries[0].on('ready', () => { + expect(cl.length()).to.be.equal(1); + cl.x('ready2 fired'); + }); + }); + }); + }); + }); + + describe('\'key_moved\' event:', () => { + it('\'key_moved\' callback does not fire for brand new locations within or outside of the GeoFirestoreQuery', (done) => { + const cl = new Checklist(['p1', 'p2'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback does not fire for locations outside of the GeoFirestoreQuery which are moved somewhere else outside of the GeoFirestoreQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved'); + }); + + geoFirestore.set({ + 'loc1': [1, 90], + 'loc2': [50, -7], + 'loc3': [16, -150] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [1, 91], + 'loc3': [-50, -50] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback does not fire for locations outside of the GeoFirestoreQuery which are moved within the GeoFirestoreQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved'); + }); + + geoFirestore.set({ + 'loc1': [1, 90], + 'loc2': [50, -7], + 'loc3': [16, -150] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [0, 0], + 'loc3': [-1, -1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback does not fire for locations within the GeoFirestoreQuery which are moved somewhere outside of the GeoFirestoreQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [1, 90], + 'loc3': [-1, -90] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback does not fires for a location within the GeoFirestoreQuery which is set to the same location', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc3 moved'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, -1] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [55, 55], + 'loc3': [1, 1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback fires for locations within the GeoFirestoreQuery which are moved somewhere else within the GeoFirestoreQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [2, 2], + 'loc3': [-1, -1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback gets passed correct location parameter', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved to 2,2', 'loc3 moved to -1,-1'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved to ' + location); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [2, 2], + 'loc3': [-1, -1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback gets passed correct distance parameter', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved (111.19 km from center)', 'loc3 moved (400.90 km from center)'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved (' + distance.toFixed(2) + ' km from center)'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [2, 2], + 'loc3': [-1, -1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback properly fires when multiple keys are at the same location within the GeoFirestoreQuery and only one of them moves somewhere else within the GeoFirestoreQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [0, 0], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [2, 2], + 'loc3': [-1, -1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_moved\' callback properly fires when a location within the GeoFirestoreQuery moves somehwere else within the GeoFirestoreQuery that is already occupied by another key', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [2, 2], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [2, 2], + 'loc3': [-1, -1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('multiple \'key_moved\' callbacks fire for locations within the GeoFirestoreQuery which are moved somewhere else within the GeoFirestoreQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved1', 'loc3 moved1', 'loc1 moved2', 'loc3 moved2'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved1'); + }); + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved2'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [2, 2], + 'loc3': [-1, -1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + }); + + describe('\'key_entered\' event:', () => { + it('\'key_entered\' callback fires when a location enters the GeoFirestoreQuery before onKeyEntered() was called', (done) => { + const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_entered\' callback fires when a location enters the GeoFirestoreQuery after onKeyEntered() was called', (done) => { + const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_entered\' callback gets passed correct location parameter', (done) => { + const cl = new Checklist(['p1', 'p2', 'loc1 entered at 2,3', 'loc4 entered at 5,5'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered at ' + location); + }); + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_entered\' callback gets passed correct distance parameter', (done) => { + const cl = new Checklist(['p1', 'p2', 'loc1 entered (157.23 km from center)', 'loc4 entered (555.66 km from center)'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered (' + distance.toFixed(2) + ' km from center)'); + }); + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_entered\' callback properly fires when multiple keys are at the same location outside the GeoFirestoreQuery and only one of them moves within the GeoFirestoreQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 entered'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + + geoFirestore.set({ + 'loc1': [50, 50], + 'loc2': [50, 50], + 'loc3': [18, -121] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set('loc1', [2, 2]); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_entered\' callback properly fires when a location outside the GeoFirestoreQuery moves somewhere within the GeoFirestoreQuery that is already occupied by another key', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 entered', 'loc3 entered'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + + geoFirestore.set({ + 'loc1': [50, 50], + 'loc2': [50, 50], + 'loc3': [0, 0] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set('loc1', [0, 0]); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('multiple \'key_entered\' callbacks fire when a location enters the GeoFirestoreQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'loc1 entered1', 'loc4 entered1', 'loc1 entered2', 'loc4 entered2'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered1'); + }); + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered2'); + }); + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return wait(100); + }).then(() => { + cl.x('p2'); + }).catch(failTestOnCaughtError); + }); + }); + + describe('\'key_exited\' event:', () => { + it('\'key_exited\' callback fires when a location leaves the GeoFirestoreQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited', 'loc4 exited'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited'); + }); + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [25, 90], + 'loc4': [25, 5] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_exited\' callback gets passed correct location parameter', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited to 25,90', 'loc4 exited to 25,5'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited to ' + location); + }); + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [5, 2], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [25, 90], + 'loc2': [5, 5], + 'loc4': [25, 5] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_exited\' callback gets passed correct distance parameter', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited (9759.01 km from center)', 'loc4 exited (2688.06 km from center)'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited (' + distance.toFixed(2) + ' km from center)'); + }); + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [5, 2], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [25, 90], + 'loc2': [5, 5], + 'loc4': [25, 5] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_exited\' callback gets passed null for location and distance parameters if the key is entirely removed from GeoFire', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + expect(location).to.be.equal(null); + expect(distance).to.be.equal(null); + cl.x(key + ' exited'); + }); + + geoFirestore.set('loc1', [2, 3]).then(() => { + cl.x('p1'); + + return geoFirestore.remove('loc1'); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_exited\' callback fires when a location within the GeoFirestoreQuery is entirely removed from GeoFire', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [2, 3] + }).then(() => { + cl.x('p1'); + + return geoFirestore.remove('loc1'); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_exited\' callback properly fires when multiple keys are at the same location inside the GeoFirestoreQuery and only one of them moves outside the GeoFirestoreQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [0, 0], + 'loc3': [18, -121] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set('loc1', [20, -55]); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_exited\' callback properly fires when a location inside the GeoFirestoreQuery moves somewhere outside the GeoFirestoreQuery that is already occupied by another key', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, 50], + 'loc3': [18, -121] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set('loc1', [18, -121]); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + + it('multiple \'key_exited\' callbacks fire when a location leaves the GeoFirestoreQuery', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited1', 'loc4 exited1', 'loc1 exited2', 'loc4 exited2'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited1'); + }); + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited2'); + }); + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [25, 90], + 'loc4': [25, 5] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3'); + }).catch(failTestOnCaughtError); + }); + }); + + describe('\'key_*\' events combined:', () => { + it('\'key_*\' event callbacks fire when used all at the same time', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc1 moved', 'loc4 exited', 'loc1 exited', 'loc5 entered'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited'); + }); + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved'); + }); + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [1, 1], + 'loc4': [25, 5] + }); + }).then(() => { + cl.x('p2'); + + return geoFirestore.set({ + 'loc1': [10, -100], + 'loc2': [50, -50], + 'loc5': [5, 5] + }); + }).then(() => { + cl.x('p3'); + + return wait(100); + }).then(() => { + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('location moving between geohash queries triggers a key_moved', (done) => { + const cl = new Checklist(['loc1 entered', 'loc2 entered', 'p1', 'loc1 moved', 'loc2 moved', 'p2'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [0, 0], radius: 1000 })); + + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited'); + }); + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved'); + }); + + geoFirestore.set({ + 'loc1': [-1, -1], + 'loc2': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [1, 1], + 'loc2': [-1, -1] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).catch(failTestOnCaughtError); + }); + }); + + describe('Cancelling GeoFirestoreQuery:', () => { + it('cancel() prevents GeoFirestoreQuery from firing any more \'key_*\' event callbacks', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 entered', 'loc4 entered', 'loc1 moved', 'loc4 exited'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited'); + }); + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved'); + }); + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [1, 1], + 'loc4': [25, 5] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3') + + geoFirestoreQueries[0].cancel(); + + return wait(1000); + }).then(() => { + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited'); + }); + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved'); + }); + + return geoFirestore.set({ + 'loc1': [10, -100], + 'loc2': [50, -50], + 'loc5': [5, 5] + }); + }).then(() => { + cl.x('p4'); + + return wait(100); + }).then(() => { + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('Calling cancel() on one GeoFirestoreQuery does not cancel other GeoQueries', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 entered1', 'loc1 entered2', 'loc4 entered1', 'loc4 entered2', 'loc1 moved1', 'loc1 moved2', 'loc4 exited1', 'loc4 exited2', 'loc1 exited2', 'loc5 entered2'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered1'); + }); + geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited1'); + }); + geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved1'); + }); + + geoFirestoreQueries[1].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered2'); + }); + geoFirestoreQueries[1].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited2'); + }); + geoFirestoreQueries[1].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved2'); + }); + + geoFirestore.set({ + 'loc1': [2, 3], + 'loc2': [50, -7], + 'loc3': [16, -150], + 'loc4': [5, 5], + 'loc5': [67, 55] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set({ + 'loc1': [1, 1], + 'loc4': [25, 5] + }); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + cl.x('p3') + + geoFirestoreQueries[0].cancel(); + + return geoFirestore.set({ + 'loc1': [10, -100], + 'loc2': [50, -50], + 'loc5': [1, 2] + }); + }).then(() => { + cl.x('p4'); + + return wait(100); + }).then(() => { + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('Calling cancel() in the middle of firing \'key_entered\' events is allowed', (done) => { + const cl = new Checklist(['p1', 'key entered', 'cancel query'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + geoFirestore.set({ + 'loc1': [1, 2], + 'loc2': [1, 3], + 'loc3': [1, 4] + }).then(() => { + cl.x('p1'); + + let numKeyEnteredEventsFired = 0; + geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x('key entered'); + numKeyEnteredEventsFired++; + if (numKeyEnteredEventsFired === 1) { + cl.x('cancel query'); + geoFirestoreQueries[0].cancel(); + } + }); + }).catch(failTestOnCaughtError); + }); + }); +}); From dd93bcde90bf33848dea9ccd2ff5678cd9b9ebb9 Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Sat, 10 Mar 2018 23:00:11 -0400 Subject: [PATCH 07/23] test(geofirestore): check location against array instead of object --- tests/geoFirestore/index.test.ts | 109 +++++++++++++++---------------- 1 file changed, 53 insertions(+), 56 deletions(-) diff --git a/tests/geoFirestore/index.test.ts b/tests/geoFirestore/index.test.ts index c6c75a71..6e9e2ae7 100755 --- a/tests/geoFirestore/index.test.ts +++ b/tests/geoFirestore/index.test.ts @@ -65,14 +65,13 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' }, - 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [ 50, 50 ], 'g': 'v0gs3y0zh7' }, - 'loc3': { '.priority': '1bpbpbpbpb', 'l': [ -90, -90 ], 'g': '1bpbpbpbpb' } + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [50, 50], 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' } }); cl.x('p4'); }).catch((error) => { - console.log(error); failTestOnCaughtError(error) }); }); @@ -94,9 +93,9 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': 'ebpcrypzxv', 'l': [ 0.254, 0 ], 'g': 'ebpcrypzxv' }, - 'loc2': { '.priority': 'v0gu2qnx15', 'l': [ 50, 50.293403 ], 'g': 'v0gu2qnx15' }, - 'loc3': { '.priority': '1cr648sfx4', 'l': [ -82.614, -90.938 ], 'g': '1cr648sfx4' } + 'loc1': { '.priority': 'ebpcrypzxv', 'l': [0.254, 0], 'g': 'ebpcrypzxv' }, + 'loc2': { '.priority': 'v0gu2qnx15', 'l': [50, 50.293403], 'g': 'v0gu2qnx15' }, + 'loc3': { '.priority': '1cr648sfx4', 'l': [-82.614, -90.938], 'g': '1cr648sfx4' } }); cl.x('p4'); @@ -124,9 +123,9 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': 's065kk0dc5', 'l': [ 2, 3 ], 'g': 's065kk0dc5' }, - 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [ 50, 50 ], 'g': 'v0gs3y0zh7' }, - 'loc3': { '.priority': '1bpbpbpbpb', 'l': [ -90, -90 ], 'g': '1bpbpbpbpb' } + 'loc1': { '.priority': 's065kk0dc5', 'l': [2, 3], 'g': 's065kk0dc5' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [50, 50], 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' } }); cl.x('p5'); @@ -154,9 +153,9 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' }, - 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [ 50, 50 ], 'g': 'v0gs3y0zh7' }, - 'loc3': { '.priority': '1bpbpbpbpb', 'l': [ -90, -90 ], 'g': '1bpbpbpbpb' } + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [50, 50], 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' } }); cl.x('p5'); @@ -180,9 +179,9 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' }, - 'loc2': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' }, - 'loc3': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' } + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' }, + 'loc3': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' } }); cl.x('p4'); @@ -234,11 +233,11 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc:1': { '.priority': 'cped3g0fur', 'l': [ 87.6, -130 ], 'g': 'cped3g0fur' }, - 'loc2': { '.priority': 'd2h376zj8h', 'l': [ 0.2358, -72.621 ], 'g': 'd2h376zj8h' }, - 'loc%!A72f()3': { '.priority': '1bpbpbpbpb', 'l': [ -90, -90 ], 'g': '1bpbpbpbpb' }, - 'loc4': { '.priority': 'cped3g0fur', 'l': [ 87.6, -130 ], 'g': 'cped3g0fur' }, - 'loc6': { '.priority': 'h50svty4es', 'l': [ -72.258, 0.953215 ], 'g': 'h50svty4es' } + 'loc:1': { '.priority': 'cped3g0fur', 'l': [87.6, -130], 'g': 'cped3g0fur' }, + 'loc2': { '.priority': 'd2h376zj8h', 'l': [0.2358, -72.621], 'g': 'd2h376zj8h' }, + 'loc%!A72f()3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' }, + 'loc4': { '.priority': 'cped3g0fur', 'l': [87.6, -130], 'g': 'cped3g0fur' }, + 'loc6': { '.priority': 'h50svty4es', 'l': [-72.258, 0.953215], 'g': 'h50svty4es' } }); cl.x('p11'); @@ -262,7 +261,7 @@ describe('GeoFirestore Tests:', () => { }); it('set() does not throw errors given valid locations', () => { - validLocations.forEach((validLocation, i) => { + validLocations.forEach((validLocation) => { expect(() => { geoFirestore.set('loc', validLocation); }).not.to.throw(); @@ -270,13 +269,13 @@ describe('GeoFirestore Tests:', () => { }); it('set() throws errors given invalid locations', () => { - invalidLocations.forEach((invalidLocation, i) => { + invalidLocations.forEach((invalidLocation) => { // Setting location to null is valid since it will remove the key if (invalidLocation !== null) { expect(() => { // @ts-ignore geoFirestore.set('loc', invalidLocation); - }).to.throw(); + }).to.throw(Error, /Invalid GeoFire location/); } }); }); @@ -307,9 +306,9 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' }, - 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [ 50, 50 ], 'g': 'v0gs3y0zh7' }, - 'loc3': { '.priority': '1bpbpbpbpb', 'l': [ -90, -90 ], 'g': '1bpbpbpbpb' } + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [50, 50], 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' } }); cl.x('p2'); @@ -329,9 +328,9 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': 'ebpcrypzxv', 'l': [ 0.254, 0 ], 'g': 'ebpcrypzxv' }, - 'loc2': { '.priority': 'v0gu2qnx15', 'l': [ 50, 50.293403 ], 'g': 'v0gu2qnx15' }, - 'loc3': { '.priority': '1cr648sfx4', 'l': [ -82.614, -90.938 ], 'g': '1cr648sfx4' } + 'loc1': { '.priority': 'ebpcrypzxv', 'l': [0.254, 0], 'g': 'ebpcrypzxv' }, + 'loc2': { '.priority': 'v0gu2qnx15', 'l': [50, 50.293403], 'g': 'v0gu2qnx15' }, + 'loc3': { '.priority': '1cr648sfx4', 'l': [-82.614, -90.938], 'g': '1cr648sfx4' } }); cl.x('p2'); @@ -357,9 +356,9 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': 's065kk0dc5', 'l': [ 2, 3 ], 'g': 's065kk0dc5' }, - 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [ 50, 50 ], 'g': 'v0gs3y0zh7' }, - 'loc3': { '.priority': '1bpbpbpbpb', 'l': [ -90, -90 ], 'g': '1bpbpbpbpb' } + 'loc1': { '.priority': 's065kk0dc5', 'l': [2, 3], 'g': 's065kk0dc5' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [50, 50], 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' } }); cl.x('p3'); @@ -385,9 +384,9 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' }, - 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [ 50, 50 ], 'g': 'v0gs3y0zh7' }, - 'loc3': { '.priority': '1bpbpbpbpb', 'l': [ -90, -90 ], 'g': '1bpbpbpbpb' } + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [50, 50], 'g': 'v0gs3y0zh7' }, + 'loc3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' } }); cl.x('p3'); @@ -407,9 +406,9 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' }, - 'loc2': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' }, - 'loc3': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' } + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' }, + 'loc2': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' }, + 'loc3': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' } }); cl.x('p2'); @@ -454,11 +453,11 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc:1': { '.priority': 'cped3g0fur', 'l': [ 87.6, -130 ], 'g': 'cped3g0fur' }, - 'loc2': { '.priority': 'd2h376zj8h', 'l': [ 0.2358, -72.621 ], 'g': 'd2h376zj8h' }, - 'loc%!A72f()3': { '.priority': '1bpbpbpbpb', 'l': [ -90, -90 ], 'g': '1bpbpbpbpb' }, - 'loc4': { '.priority': 'cped3g0fur', 'l': [ 87.6, -130 ], 'g': 'cped3g0fur' }, - 'loc6': { '.priority': 'h50svty4es', 'l': [ -72.258, 0.953215 ], 'g': 'h50svty4es' } + 'loc:1': { '.priority': 'cped3g0fur', 'l': [87.6, -130], 'g': 'cped3g0fur' }, + 'loc2': { '.priority': 'd2h376zj8h', 'l': [0.2358, -72.621], 'g': 'd2h376zj8h' }, + 'loc%!A72f()3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' }, + 'loc4': { '.priority': 'cped3g0fur', 'l': [87.6, -130], 'g': 'cped3g0fur' }, + 'loc6': { '.priority': 'h50svty4es', 'l': [-72.258, 0.953215], 'g': 'h50svty4es' } }); cl.x('p6'); @@ -497,7 +496,7 @@ describe('GeoFirestore Tests:', () => { }); it('set() does not throw errors given valid locations', () => { - validLocations.forEach((validLocation, i) => { + validLocations.forEach((validLocation) => { expect(() => { geoFirestore.set({ 'loc': validLocation @@ -507,14 +506,14 @@ describe('GeoFirestore Tests:', () => { }); it('set() throws errors given invalid locations', () => { - invalidLocations.forEach((invalidLocation, i) => { + invalidLocations.forEach((invalidLocation) => { // Setting location to null is valid since it will remove the key if (invalidLocation !== null) { expect(() => { geoFirestore.set({ 'loc': invalidLocation }); - }).to.throw(); + }).to.throw(Error, /Invalid GeoFire location/); } }); }); @@ -525,7 +524,6 @@ describe('GeoFirestore Tests:', () => { const cl = new Checklist(['p1'], expect, done); geoFirestore.get('loc1').then((ref) => { - console.log(ref) cl.x('p1'); }); }); @@ -603,7 +601,6 @@ describe('GeoFirestore Tests:', () => { return geoFirestore.get('loc1'); }).then((location) => { - console.log('location', location); expect(location).to.equal(null); cl.x('p4'); @@ -611,7 +608,7 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc2': { '.priority': 's065kk0dc5', 'l': [ 2, 3 ], 'g': 's065kk0dc5' } + 'loc2': { '.priority': 's065kk0dc5', 'l': [2, 3], 'g': 's065kk0dc5' } }); cl.x('p5'); @@ -643,7 +640,7 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' } + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' } }); cl.x('p5'); @@ -713,7 +710,7 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' } + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' } }); cl.x('p4'); @@ -748,7 +745,7 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc2': { '.priority': 's065kk0dc5', 'l': [ 2, 3 ], 'g': 's065kk0dc5' } + 'loc2': { '.priority': 's065kk0dc5', 'l': [2, 3], 'g': 's065kk0dc5' } }); cl.x('p5'); @@ -780,7 +777,7 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' } + 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' } }); cl.x('p5'); @@ -804,8 +801,8 @@ describe('GeoFirestore Tests:', () => { return getFirestoreData(); }).then((firebaseData) => { expect(firebaseData).to.deep.equal({ - 'loc2': { '.priority': 's065kk0dc5', 'l': [ 2, 3 ], 'g': 's065kk0dc5' }, - 'loc3': { '.priority': '7zzzzzzzzz', 'l': [ 0, 0 ], 'g': '7zzzzzzzzz' } + 'loc2': { '.priority': 's065kk0dc5', 'l': [2, 3], 'g': 's065kk0dc5' }, + 'loc3': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' } }); cl.x('p3'); From 8c007d81fba67dda70c7ba6b3511f3756eb182ab Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Sat, 10 Mar 2018 23:01:18 -0400 Subject: [PATCH 08/23] test: fix tests for geofire callbacks and implement for geofirestore --- tests/common.ts | 4 +- ...ts => geoFireCallbackRegistration.test.ts} | 38 +-- .../geoFirestoreCallbackRegistration.test.ts | 253 ++++++++++++++++++ 3 files changed, 274 insertions(+), 21 deletions(-) rename tests/tools/{callbackRegistration.test.ts => geoFireCallbackRegistration.test.ts} (77%) create mode 100644 tests/tools/geoFirestoreCallbackRegistration.test.ts diff --git a/tests/common.ts b/tests/common.ts index 607ed93f..3b020e81 100644 --- a/tests/common.ts +++ b/tests/common.ts @@ -47,7 +47,7 @@ export function beforeEachHelper(done) { // Create a new GeoFire instance geoFire = new GeoFire(geoFireRef); - // Reset the GeoQueries + // Reset the GeoFireQueries geoFireQueries = []; done(); @@ -59,7 +59,7 @@ export function beforeEachHelperFirestore(done) { // Create a new GeoFire instance geoFirestore = new GeoFirestore(geoFirestoreRef); - // Reset the GeoQueries + // Reset the GeoFirestoreQueries geoFirestoreQueries = []; done(); diff --git a/tests/tools/callbackRegistration.test.ts b/tests/tools/geoFireCallbackRegistration.test.ts similarity index 77% rename from tests/tools/callbackRegistration.test.ts rename to tests/tools/geoFireCallbackRegistration.test.ts index 65e44453..04a5f7a7 100644 --- a/tests/tools/callbackRegistration.test.ts +++ b/tests/tools/geoFireCallbackRegistration.test.ts @@ -1,14 +1,14 @@ import { GeoCallbackRegistration } from '../../src/tools/callbackRegistration'; import { afterEachHelper, beforeEachHelper, Checklist, - failTestOnCaughtError, geoFire, geoQueries, wait + failTestOnCaughtError, geoFire, geoFireQueries, wait } from '../common'; import * as chai from 'chai'; const expect = chai.expect; -describe('GeoCallbackRegistration Tests:', () => { +describe('GeoFire GeoCallbackRegistration Tests:', () => { // Reset the Firebase before each test beforeEach((done) => { beforeEachHelper(done); @@ -33,9 +33,9 @@ describe('GeoCallbackRegistration Tests:', () => { it('\'key_moved\' registrations can be cancelled', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 moved'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - const onKeyMovedRegistration = geoQueries[0].on('key_moved', (key, location, distance) => { + const onKeyMovedRegistration = geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -68,9 +68,9 @@ describe('GeoCallbackRegistration Tests:', () => { it('\'key_entered\' registrations can be cancelled', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - const onKeyEnteredRegistration = geoQueries[0].on('key_entered', (key, location, distance) => { + const onKeyEnteredRegistration = geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -99,9 +99,9 @@ describe('GeoCallbackRegistration Tests:', () => { it('\'key_exited\' registrations can be cancelled', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 exited'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - const onKeyExitedRegistration = geoQueries[0].on('key_exited', (key, location, distance) => { + const onKeyExitedRegistration = geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -134,12 +134,12 @@ describe('GeoCallbackRegistration Tests:', () => { it('Cancelling a \'key_moved\' registration does not cancel all \'key_moved\' callbacks', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 moved1', 'loc1 moved2', 'loc3 moved2'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - const onKeyMovedRegistration1 = geoQueries[0].on('key_moved', (key, location, distance) => { + const onKeyMovedRegistration1 = geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved1'); }); - const onKeyMovedRegistration2 = geoQueries[0].on('key_moved', (key, location, distance) => { + const onKeyMovedRegistration2 = geoFireQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved2'); }); @@ -172,12 +172,12 @@ describe('GeoCallbackRegistration Tests:', () => { it('Cancelling a \'key_entered\' registration does not cancel all \'key_entered\' callbacks', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered1', 'loc1 entered2', 'loc3 entered2'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - const onKeyEnteredRegistration1 = geoQueries[0].on('key_entered', (key, location, distance) => { + const onKeyEnteredRegistration1 = geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered1'); }); - const onKeyEnteredRegistration2 = geoQueries[0].on('key_entered', (key, location, distance) => { + const onKeyEnteredRegistration2 = geoFireQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered2'); }); @@ -206,12 +206,12 @@ describe('GeoCallbackRegistration Tests:', () => { it('Cancelling a \'key_exited\' registration does not cancel all \'key_exited\' callbacks', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 exited1', 'loc1 exited2', 'loc3 exited2'], expect, done); - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - const onKeyExitedRegistration1 = geoQueries[0].on('key_exited', (key, location, distance) => { + const onKeyExitedRegistration1 = geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited1'); }); - const onKeyExitedRegistration2 = geoQueries[0].on('key_exited', (key, location, distance) => { + const onKeyExitedRegistration2 = geoFireQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited2'); }); @@ -242,9 +242,9 @@ describe('GeoCallbackRegistration Tests:', () => { }); it('Calling cancel on a GeoCallbackRegistration twice does not throw', () => { - geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - const onKeyExitedRegistration = geoQueries[0].on('key_exited', () => { }); + const onKeyExitedRegistration = geoFireQueries[0].on('key_exited', () => { }); expect(() => onKeyExitedRegistration.cancel()).not.throw(); expect(() => onKeyExitedRegistration.cancel()).not.throw(); diff --git a/tests/tools/geoFirestoreCallbackRegistration.test.ts b/tests/tools/geoFirestoreCallbackRegistration.test.ts new file mode 100644 index 00000000..63880ef5 --- /dev/null +++ b/tests/tools/geoFirestoreCallbackRegistration.test.ts @@ -0,0 +1,253 @@ +import { GeoCallbackRegistration } from '../../src/tools/callbackRegistration'; +import { + afterEachHelperFirestore, beforeEachHelperFirestore, Checklist, + failTestOnCaughtError, geoFirestore, geoFirestoreQueries, wait +} from '../common'; + +import * as chai from 'chai'; + +const expect = chai.expect; + +describe('GeoFirestore GeoCallbackRegistration Tests:', () => { + // Reset the Firestore before each test + beforeEach((done) => { + beforeEachHelperFirestore(done); + }); + + afterEach((done) => { + afterEachHelperFirestore(done); + }); + + describe('Constructor:', () => { + it('Constructor throws error given non-function', () => { + const createCallbackRegistration = () => { + // @ts-ignore + new GeoCallbackRegistration('nonFunction'); + } + + expect(() => createCallbackRegistration()).to.throw(null, 'callback must be a function'); + }); + }); + + describe('Cancelling event callbacks:', () => { + it('\'key_moved\' registrations can be cancelled', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 moved'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + const onKeyMovedRegistration = geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set('loc1', [2, 2]); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + onKeyMovedRegistration.cancel(); + cl.x('p3'); + + return geoFirestore.set('loc3', [1, 2]); + }).then(() => { + cl.x('p4'); + + return wait(100); + }).then(() => { + cl.x('p5'); + }).catch(failTestOnCaughtError);; + }); + + it('\'key_entered\' registrations can be cancelled', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + const onKeyEnteredRegistration = geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [80, 80] + }).then(() => { + cl.x('p1'); + + return wait(100); + }).then(() => { + onKeyEnteredRegistration.cancel(); + cl.x('p2'); + + return geoFirestore.set('loc3', [1, 2]); + }).then(() => { + cl.x('p3'); + + return wait(100); + }).then(() => { + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('\'key_exited\' registrations can be cancelled', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 exited'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + const onKeyExitedRegistration = geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set('loc1', [80, 80]); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + onKeyExitedRegistration.cancel(); + cl.x('p3'); + + return geoFirestore.set('loc3', [-80, -80]); + }).then(() => { + cl.x('p4'); + + return wait(100); + }).then(() => { + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('Cancelling a \'key_moved\' registration does not cancel all \'key_moved\' callbacks', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 moved1', 'loc1 moved2', 'loc3 moved2'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + const onKeyMovedRegistration1 = geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved1'); + }); + const onKeyMovedRegistration2 = geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { + cl.x(key + ' moved2'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set('loc1', [2, 2]); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + onKeyMovedRegistration1.cancel(); + cl.x('p3'); + + return geoFirestore.set('loc3', [1, 2]); + }).then(() => { + cl.x('p4'); + + return wait(100); + }).then(() => { + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('Cancelling a \'key_entered\' registration does not cancel all \'key_entered\' callbacks', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered1', 'loc1 entered2', 'loc3 entered2'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + const onKeyEnteredRegistration1 = geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered1'); + }); + const onKeyEnteredRegistration2 = geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { + cl.x(key + ' entered2'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [80, 80] + }).then(() => { + cl.x('p1'); + + return wait(100); + }).then(() => { + onKeyEnteredRegistration1.cancel(); + cl.x('p2'); + + return geoFirestore.set('loc3', [1, 2]); + }).then(() => { + cl.x('p3'); + + return wait(100); + }).then(() => { + cl.x('p4'); + }).catch(failTestOnCaughtError); + }); + + it('Cancelling a \'key_exited\' registration does not cancel all \'key_exited\' callbacks', (done) => { + const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 exited1', 'loc1 exited2', 'loc3 exited2'], expect, done); + + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + const onKeyExitedRegistration1 = geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited1'); + }); + const onKeyExitedRegistration2 = geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { + cl.x(key + ' exited2'); + }); + + geoFirestore.set({ + 'loc1': [0, 0], + 'loc2': [50, -7], + 'loc3': [1, 1] + }).then(() => { + cl.x('p1'); + + return geoFirestore.set('loc1', [80, 80]); + }).then(() => { + cl.x('p2'); + + return wait(100); + }).then(() => { + onKeyExitedRegistration1.cancel(); + cl.x('p3'); + + return geoFirestore.set('loc3', [-80, -80]); + }).then(() => { + cl.x('p4'); + + return wait(100); + }).then(() => { + cl.x('p5'); + }).catch(failTestOnCaughtError); + }); + + it('Calling cancel on a GeoCallbackRegistration twice does not throw', () => { + geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); + + const onKeyExitedRegistration = geoFirestoreQueries[0].on('key_exited', () => { }); + + expect(() => onKeyExitedRegistration.cancel()).not.throw(); + expect(() => onKeyExitedRegistration.cancel()).not.throw(); + }); + }); +}); From ffb377aab702dc80e45006b5dd1daf8f6f0dbee7 Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Sat, 10 Mar 2018 23:01:58 -0400 Subject: [PATCH 09/23] fix(firestore): fix single location removal by set function --- src/geoFirestore/index.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/geoFirestore/index.ts b/src/geoFirestore/index.ts index c6d2ab4c..b1c357d4 100644 --- a/src/geoFirestore/index.ts +++ b/src/geoFirestore/index.ts @@ -85,14 +85,16 @@ export class GeoFirestore { * @returns A promise that is fulfilled when the write is complete. */ public set(keyOrLocations: string | any, location?: number[]): Promise { - if (typeof keyOrLocations === 'string' && location && location.length !== 0) { + if (typeof keyOrLocations === 'string' && keyOrLocations.length !== 0) { validateKey(keyOrLocations); - validateLocation(location); - const geohash: string = encodeGeohash(location); - return this._collectionRef.doc(keyOrLocations).set(encodeGeoFireObject(location, geohash)); - } else if (typeof keyOrLocations === 'string') { - validateKey(keyOrLocations); - return this._collectionRef.doc(keyOrLocations).delete(); + if (location === null) { + // Setting location to null is valid since it will remove the key + return this._collectionRef.doc(keyOrLocations).delete(); + } else { + validateLocation(location); + const geohash: string = encodeGeohash(location); + return this._collectionRef.doc(keyOrLocations).set(encodeGeoFireObject(location, geohash)); + } } else if (typeof keyOrLocations === 'object') { if (typeof location !== 'undefined') { throw new Error('The location argument should not be used if you pass an object to set().'); From 4a0d31278e85aa86d8ac4765f0f81d3474563bbf Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Sat, 10 Mar 2018 23:03:18 -0400 Subject: [PATCH 10/23] refactor: change vars to consts and use in instead of hasOwnProperty --- src/geoFire/query.ts | 12 ++++++------ src/geoFirestore/query.ts | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/geoFire/query.ts b/src/geoFire/query.ts index 1696580a..d08666b5 100644 --- a/src/geoFire/query.ts +++ b/src/geoFire/query.ts @@ -254,7 +254,7 @@ export class GeoFireQuery { */ private _childRemovedCallback(locationDataSnapshot: firebase.database.DataSnapshot): void { const key: string = geoFireGetKey(locationDataSnapshot); - if (this._locationsTracked.hasOwnProperty(key)) { + if (key in this._locationsTracked) { this._firebaseRef.child(key).once('value', (snapshot: firebase.database.DataSnapshot) => { const location: number[] = (snapshot.val() === null) ? null : decodeGeoFireObject(snapshot.val()); const geohash: string = (location !== null) ? encodeGeohash(location) : null; @@ -276,7 +276,7 @@ export class GeoFireQuery { keys.forEach((geohashQueryStr: string) => { const queryState: any = this._currentGeohashesQueried[geohashQueryStr]; if (queryState.active === false) { - var query = this._stringToQuery(geohashQueryStr); + const query = this._stringToQuery(geohashQueryStr); // Delete the geohash since it should no longer be queried this._cancelGeohashQuery(query, queryState); delete this._currentGeohashesQueried[geohashQueryStr]; @@ -340,8 +340,8 @@ export class GeoFireQuery { private _geohashInSomeQuery(geohash: string): boolean { const keys: string[] = Object.keys(this._currentGeohashesQueried); for (const queryStr of keys) { - if (this._currentGeohashesQueried.hasOwnProperty(queryStr)) { - var query = this._stringToQuery(queryStr); + if (queryStr in this._currentGeohashesQueried) { + const query = this._stringToQuery(queryStr); if (geohash >= query[0] && geohash <= query[1]) { return true; } @@ -500,8 +500,8 @@ export class GeoFireQuery { validateLocation(location); // Get the key and location let distanceFromCenter: number, isInQuery; - var wasInQuery: boolean = (this._locationsTracked.hasOwnProperty(key)) ? this._locationsTracked[key].isInQuery : false; - var oldLocation: number[] = (this._locationsTracked.hasOwnProperty(key)) ? this._locationsTracked[key].location : null; + const wasInQuery: boolean = (key in this._locationsTracked) ? this._locationsTracked[key].isInQuery : false; + const oldLocation: number[] = (key in this._locationsTracked) ? this._locationsTracked[key].location : null; // Determine if the location is within this query distanceFromCenter = GeoFire.distance(location, this._center); diff --git a/src/geoFirestore/query.ts b/src/geoFirestore/query.ts index 5900a846..79784651 100644 --- a/src/geoFirestore/query.ts +++ b/src/geoFirestore/query.ts @@ -251,7 +251,7 @@ export class GeoFirestoreQuery { */ private _childRemovedCallback(locationDataSnapshot: firebase.firestore.DocumentSnapshot): void { const key: string = geoFirestoreGetKey(locationDataSnapshot); - if (this._locationsTracked.hasOwnProperty(key)) { + if (key in this._locationsTracked) { this._collectionRef.doc(key).get().then((snapshot: firebase.firestore.DocumentSnapshot) => { const location: number[] = (snapshot.data() === null) ? null : decodeGeoFireObject(snapshot.data()); const geohash: string = (location !== null) ? encodeGeohash(location) : null; @@ -273,7 +273,7 @@ export class GeoFirestoreQuery { keys.forEach((geohashQueryStr: string) => { const queryState: any = this._currentGeohashesQueried[geohashQueryStr]; if (queryState.active === false) { - var query = this._stringToQuery(geohashQueryStr); + const query = this._stringToQuery(geohashQueryStr); // Delete the geohash since it should no longer be queried this._cancelGeohashQuery(query, queryState); delete this._currentGeohashesQueried[geohashQueryStr]; @@ -337,8 +337,8 @@ export class GeoFirestoreQuery { private _geohashInSomeQuery(geohash: string): boolean { const keys: string[] = Object.keys(this._currentGeohashesQueried); for (const queryStr of keys) { - if (this._currentGeohashesQueried.hasOwnProperty(queryStr)) { - var query = this._stringToQuery(queryStr); + if (queryStr in this._currentGeohashesQueried) { + const query = this._stringToQuery(queryStr); if (geohash >= query[0] && geohash <= query[1]) { return true; } @@ -505,8 +505,8 @@ export class GeoFirestoreQuery { validateLocation(location); // Get the key and location let distanceFromCenter: number, isInQuery; - var wasInQuery: boolean = (this._locationsTracked.hasOwnProperty(key)) ? this._locationsTracked[key].isInQuery : false; - var oldLocation: number[] = (this._locationsTracked.hasOwnProperty(key)) ? this._locationsTracked[key].location : null; + const wasInQuery: boolean = (key in this._locationsTracked) ? this._locationsTracked[key].isInQuery : false; + const oldLocation: number[] = (key in this._locationsTracked) ? this._locationsTracked[key].location : null; // Determine if the location is within this query distanceFromCenter = GeoFirestore.distance(location, this._center); From e750c65f23947de979ec359b365d47e70bfefd31 Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Sun, 11 Mar 2018 00:01:15 -0400 Subject: [PATCH 11/23] fix(firestore): set data from snapshot to variable to pass into decode --- src/geoFirestore/query.ts | 9 ++++++--- src/tools/utils.ts | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/geoFirestore/query.ts b/src/geoFirestore/query.ts index 79784651..efed2c0a 100644 --- a/src/geoFirestore/query.ts +++ b/src/geoFirestore/query.ts @@ -232,7 +232,8 @@ export class GeoFirestoreQuery { * @param locationDataSnapshot A snapshot of the data stored for this location. */ private _childAddedCallback(locationDataSnapshot: firebase.firestore.DocumentSnapshot): void { - this._updateLocation(geoFirestoreGetKey(locationDataSnapshot), decodeGeoFireObject(locationDataSnapshot.data())); + const data = locationDataSnapshot.data(); + this._updateLocation(geoFirestoreGetKey(locationDataSnapshot), decodeGeoFireObject(data)); } /** @@ -241,7 +242,8 @@ export class GeoFirestoreQuery { * @param locationDataSnapshot A snapshot of the data stored for this location. */ private _childChangedCallback(locationDataSnapshot: firebase.firestore.DocumentSnapshot): void { - this._updateLocation(geoFirestoreGetKey(locationDataSnapshot), decodeGeoFireObject(locationDataSnapshot.data())); + const data = locationDataSnapshot.data(); + this._updateLocation(geoFirestoreGetKey(locationDataSnapshot), decodeGeoFireObject(data)); } /** @@ -253,7 +255,8 @@ export class GeoFirestoreQuery { const key: string = geoFirestoreGetKey(locationDataSnapshot); if (key in this._locationsTracked) { this._collectionRef.doc(key).get().then((snapshot: firebase.firestore.DocumentSnapshot) => { - const location: number[] = (snapshot.data() === null) ? null : decodeGeoFireObject(snapshot.data()); + const data = (!snapshot.exists) ? null : snapshot.data(); + const location: number[] = (!snapshot.exists) ? null : decodeGeoFireObject(data); const geohash: string = (location !== null) ? encodeGeohash(location) : null; // Only notify observers if key is not part of any other geohash query or this actually might not be // a key exited event, but a key moved or entered event. These events will be triggered by updates diff --git a/src/tools/utils.ts b/src/tools/utils.ts index 40c73e19..df06f710 100644 --- a/src/tools/utils.ts +++ b/src/tools/utils.ts @@ -415,10 +415,10 @@ export function encodeGeoFireObject(location: number[], geohash: string): GeoFir * @returns The location as [latitude, longitude] pair or null if decoding fails. */ export function decodeGeoFireObject(geoFireObj: GeoFireObj): number[] { - if (geoFireObj !== null && geoFireObj.hasOwnProperty('l') && Array.isArray(geoFireObj.l) && geoFireObj.l.length === 2) { + if (geoFireObj && 'l' in geoFireObj && Array.isArray(geoFireObj.l) && geoFireObj.l.length === 2) { return geoFireObj.l; } else { - throw new Error('Unexpected GeoFire location object encountered: ' + JSON.stringify(geoFireObj)); + throw new Error('Unexpected location object encountered: ' + JSON.stringify(geoFireObj)); } } From 261445e60ea2179f4735639f4b6c835bbbb6a354 Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Sun, 11 Mar 2018 01:00:36 -0400 Subject: [PATCH 12/23] refactor: renamed folders and small tweaks --- package.json | 4 ++-- src/{geoFire => firebase}/index.ts | 0 src/{geoFire => firebase}/query.ts | 0 src/{geoFirestore => firestore}/index.ts | 0 src/{geoFirestore => firestore}/query.ts | 0 src/index.ts | 4 ++-- tests/common.ts | 10 +++++----- tests/{geoFire => firebase}/index.test.ts | 4 ++-- tests/{geoFire => firebase}/query.test.ts | 0 tests/{geoFirestore => firestore}/index.test.ts | 4 ++-- tests/{geoFirestore => firestore}/query.test.ts | 7 +++---- tests/tools/geoFirestoreCallbackRegistration.test.ts | 2 +- tests/tools/utils.test.ts | 2 +- 13 files changed, 18 insertions(+), 19 deletions(-) rename src/{geoFire => firebase}/index.ts (100%) rename src/{geoFire => firebase}/query.ts (100%) rename src/{geoFirestore => firestore}/index.ts (100%) rename src/{geoFirestore => firestore}/query.ts (100%) rename tests/{geoFire => firebase}/index.test.ts (99%) rename tests/{geoFire => firebase}/query.test.ts (100%) rename tests/{geoFirestore => firestore}/index.test.ts (99%) rename tests/{geoFirestore => firestore}/query.test.ts (99%) diff --git a/package.json b/package.json index a935c4f3..94030401 100644 --- a/package.json +++ b/package.json @@ -45,9 +45,9 @@ "uglify-js": "^3.3.13" }, "scripts": { - "browserify": "browserify dist/geoFire/index.js -o dist/browser/geofire.js && browserify dist/geoFirestore/index.js -o dist/browser/geofirestore.js", + "browserify": "browserify dist/firebase/index.js -o dist/browser/geofire.js && browserify dist/firestore/index.js -o dist/browser/geofirestore.js", "build": "rm -rf ./dist && tsc && npm run browserify && npm run uglify", - "test": "mocha --reporter spec -r ts-node/register -r jsdom-global/register --timeout 10000 --exit 'tests/**/*.test.ts'", + "test": "mocha --reporter spec -r ts-node/register -r jsdom-global/register --timeout 20000 --exit 'tests/**/*.test.ts'", "travis": "npm run test && npm run build", "uglify": "uglifyjs dist/browser/geofire.js -c -m -o dist/browser/geofire.min.js && uglifyjs dist/browser/geofirestore.js -c -m -o dist/browser/geofirestore.min.js" } diff --git a/src/geoFire/index.ts b/src/firebase/index.ts similarity index 100% rename from src/geoFire/index.ts rename to src/firebase/index.ts diff --git a/src/geoFire/query.ts b/src/firebase/query.ts similarity index 100% rename from src/geoFire/query.ts rename to src/firebase/query.ts diff --git a/src/geoFirestore/index.ts b/src/firestore/index.ts similarity index 100% rename from src/geoFirestore/index.ts rename to src/firestore/index.ts diff --git a/src/geoFirestore/query.ts b/src/firestore/query.ts similarity index 100% rename from src/geoFirestore/query.ts rename to src/firestore/query.ts diff --git a/src/index.ts b/src/index.ts index e9b19b41..7c1c994c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ export * from './interfaces'; -export { GeoFire } from './geoFire'; -export { GeoFirestore } from './geoFirestore'; \ No newline at end of file +export { GeoFire } from './firebase'; +export { GeoFirestore } from './firestore'; \ No newline at end of file diff --git a/tests/common.ts b/tests/common.ts index 3b020e81..9e074d1d 100644 --- a/tests/common.ts +++ b/tests/common.ts @@ -3,8 +3,8 @@ import * as firebase from 'firebase'; import 'firebase/firestore'; import { GeoFire, GeoFirestore } from '../src'; -import { GeoFireQuery } from '../src/geoFire/query'; -import { GeoFirestoreQuery } from '../src/geoFirestore/query'; +import { GeoFireQuery } from '../src/firebase/query'; +import { GeoFirestoreQuery } from '../src/firestore/query'; /*************/ /* GLOBALS */ @@ -31,9 +31,9 @@ export let geoFirestoreRef: firebase.firestore.CollectionReference, // Initialize Firebase const config = { - apiKey: 'AIzaSyAWJ1lpvNJgAwFYR0TRWyySeaMswTKnDOU', - databaseURL: 'https://geofire-95a87.firebaseio.com', - projectId: 'geofire-95a87' + apiKey: 'AIzaSyDPmGjw0Igt75VpzGmfgdqmHornPneV7zQ', + databaseURL: 'https://geofire-5f3fc.firebaseio.com', + projectId: 'geofire-5f3fc', }; firebase.initializeApp(config); diff --git a/tests/geoFire/index.test.ts b/tests/firebase/index.test.ts similarity index 99% rename from tests/geoFire/index.test.ts rename to tests/firebase/index.test.ts index 818058b1..4daf5139 100755 --- a/tests/geoFire/index.test.ts +++ b/tests/firebase/index.test.ts @@ -1,7 +1,7 @@ import * as chai from 'chai'; -import { GeoFire } from '../../src/geoFire'; -import { GeoFireQuery } from '../../src/geoFire/query'; +import { GeoFire } from '../../src/firebase'; +import { GeoFireQuery } from '../../src/firebase/query'; import { afterEachHelper, beforeEachHelper, Checklist, failTestOnCaughtError, geoFire, geoFireRef, getFirebaseData, geoFireQueries, invalidFirebaseRefs, invalidKeys, invalidLocations, invalidQueryCriterias, validKeys, validLocations, validQueryCriterias diff --git a/tests/geoFire/query.test.ts b/tests/firebase/query.test.ts similarity index 100% rename from tests/geoFire/query.test.ts rename to tests/firebase/query.test.ts diff --git a/tests/geoFirestore/index.test.ts b/tests/firestore/index.test.ts similarity index 99% rename from tests/geoFirestore/index.test.ts rename to tests/firestore/index.test.ts index 6e9e2ae7..2c5c7267 100755 --- a/tests/geoFirestore/index.test.ts +++ b/tests/firestore/index.test.ts @@ -1,7 +1,7 @@ import * as chai from 'chai'; -import { GeoFirestore } from '../../src/geoFirestore'; -import { GeoFirestoreQuery } from '../../src/geoFirestore/query'; +import { GeoFirestore } from '../../src/firestore'; +import { GeoFirestoreQuery } from '../../src/firestore/query'; import { afterEachHelperFirestore, beforeEachHelperFirestore, Checklist, failTestOnCaughtError, geoFirestore, geoFirestoreRef, getFirestoreData, geoFirestoreQueries, invalidFirebaseRefs, invalidKeys, invalidLocations, invalidQueryCriterias, validKeys, validLocations, validQueryCriterias diff --git a/tests/geoFirestore/query.test.ts b/tests/firestore/query.test.ts similarity index 99% rename from tests/geoFirestore/query.test.ts rename to tests/firestore/query.test.ts index da720d95..9a1ac814 100644 --- a/tests/geoFirestore/query.test.ts +++ b/tests/firestore/query.test.ts @@ -1054,7 +1054,7 @@ describe('GeoFirestoreQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_exited\' callback gets passed null for location and distance parameters if the key is entirely removed from GeoFire', (done) => { + it('\'key_exited\' callback gets passed null for location and distance parameters if the key is entirely removed from GeoFirestore', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); @@ -1078,7 +1078,7 @@ describe('GeoFirestoreQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_exited\' callback fires when a location within the GeoFirestoreQuery is entirely removed from GeoFire', (done) => { + it('\'key_exited\' callback fires when a location within the GeoFirestoreQuery is entirely removed from GeoFirestore', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); @@ -1376,8 +1376,7 @@ describe('GeoFirestoreQuery Tests:', () => { return wait(100); }).then(() => { - cl.x('p3') - + cl.x('p3'); geoFirestoreQueries[0].cancel(); return geoFirestore.set({ diff --git a/tests/tools/geoFirestoreCallbackRegistration.test.ts b/tests/tools/geoFirestoreCallbackRegistration.test.ts index 63880ef5..45ca14a6 100644 --- a/tests/tools/geoFirestoreCallbackRegistration.test.ts +++ b/tests/tools/geoFirestoreCallbackRegistration.test.ts @@ -121,7 +121,7 @@ describe('GeoFirestore GeoCallbackRegistration Tests:', () => { onKeyExitedRegistration.cancel(); cl.x('p3'); - return geoFirestore.set('loc3', [-80, -80]); + return wait(100).then(() => geoFirestore.set('loc3', [-80, -80])); }).then(() => { cl.x('p4'); diff --git a/tests/tools/utils.test.ts b/tests/tools/utils.test.ts index d1da57e0..b68546ce 100644 --- a/tests/tools/utils.test.ts +++ b/tests/tools/utils.test.ts @@ -1,6 +1,6 @@ import * as chai from 'chai'; -import { GeoFire } from '../../src/geoFire'; +import { GeoFire } from '../../src/firebase'; import { boundingBoxBits, degreesToRadians, encodeGeohash, geohashQuery, geohashQueries, g_GEOHASH_PRECISION, metersToLongitudeDegrees, validateCriteria, validateGeohash, validateKey, validateLocation, wrapLongitude From 361fc5b4cb605678d2a933d43d539f4d7766f17c Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Sun, 11 Mar 2018 02:40:11 -0400 Subject: [PATCH 13/23] test: reintroduce coveralls support with tweaks --- .gitignore | 5 +- .travis.yml | 1 + package-lock.json | 1619 +++++++++++++++++ package.json | 19 +- {tests => test}/common.ts | 9 +- {tests => test}/firebase/index.test.ts | 0 {tests => test}/firebase/query.test.ts | 0 {tests => test}/firestore/index.test.ts | 0 {tests => test}/firestore/query.test.ts | 0 test/mocha.opts | 6 + .../tools/geoFireCallbackRegistration.test.ts | 0 .../geoFirestoreCallbackRegistration.test.ts | 5 +- {tests => test}/tools/utils.test.ts | 0 tsconfig.json | 2 +- 14 files changed, 1657 insertions(+), 9 deletions(-) rename {tests => test}/common.ts (96%) rename {tests => test}/firebase/index.test.ts (100%) rename {tests => test}/firebase/query.test.ts (100%) rename {tests => test}/firestore/index.test.ts (100%) rename {tests => test}/firestore/query.test.ts (100%) create mode 100644 test/mocha.opts rename {tests => test}/tools/geoFireCallbackRegistration.test.ts (100%) rename {tests => test}/tools/geoFirestoreCallbackRegistration.test.ts (98%) rename {tests => test}/tools/utils.test.ts (100%) diff --git a/.gitignore b/.gitignore index 8901d41b..7ab08609 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ dist/ node_modules/ -tests/coverage/ -.DS_Store \ No newline at end of file +coverage/ +.DS_Store +.nyc_output/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 767481e4..a1f9a0dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,4 +7,5 @@ install: script: - npm run test after_success: + - npm run coverage - npm run build diff --git a/package-lock.json b/package-lock.json index 56541918..7b75d10f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -908,6 +908,19 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "coveralls": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.0.tgz", + "integrity": "sha512-ZppXR9y5PraUOrf/DzHJY6gzNUhXYE3b9D43xEXs4QYZ7/Oe0Gy0CS+IPKWFfvQFXB3RG9QduaQUFehzSpGAFw==", + "dev": true, + "requires": { + "js-yaml": "3.11.0", + "lcov-parse": "0.0.10", + "log-driver": "1.2.7", + "minimist": "1.2.0", + "request": "2.83.0" + } + }, "create-ecdh": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", @@ -2980,6 +2993,12 @@ "invert-kv": "1.0.0" } }, + "lcov-parse": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-0.0.10.tgz", + "integrity": "sha1-GwuP+ayceIklBYK3C3ExXZ2m2aM=", + "dev": true + }, "lcov-result-merger": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lcov-result-merger/-/lcov-result-merger-2.0.0.tgz", @@ -3040,6 +3059,12 @@ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, + "log-driver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", + "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", + "dev": true + }, "long": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", @@ -3264,6 +3289,1600 @@ "integrity": "sha512-IKdSTiDWCarf2JTS5e9e2+5tPZGdkRJ79XjYV0pzK8Q9BpsFyBq1RGKxzs7Q8UBushGw7m6TzVKz6fcY99iSWw==", "dev": true }, + "nyc": { + "version": "11.4.1", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-11.4.1.tgz", + "integrity": "sha512-5eCZpvaksFVjP2rt1r60cfXmt3MUtsQDw8bAzNqNEr4WLvUMLgiVENMf/B9bE9YAX0mGVvaGA3v9IS9ekNqB1Q==", + "dev": true, + "requires": { + "archy": "1.0.0", + "arrify": "1.0.1", + "caching-transform": "1.0.1", + "convert-source-map": "1.5.1", + "debug-log": "1.0.1", + "default-require-extensions": "1.0.0", + "find-cache-dir": "0.1.1", + "find-up": "2.1.0", + "foreground-child": "1.5.6", + "glob": "7.1.2", + "istanbul-lib-coverage": "1.1.1", + "istanbul-lib-hook": "1.1.0", + "istanbul-lib-instrument": "1.9.1", + "istanbul-lib-report": "1.1.2", + "istanbul-lib-source-maps": "1.2.2", + "istanbul-reports": "1.1.3", + "md5-hex": "1.3.0", + "merge-source-map": "1.0.4", + "micromatch": "2.3.11", + "mkdirp": "0.5.1", + "resolve-from": "2.0.0", + "rimraf": "2.6.2", + "signal-exit": "3.0.2", + "spawn-wrap": "1.4.2", + "test-exclude": "4.1.1", + "yargs": "10.0.3", + "yargs-parser": "8.0.0" + }, + "dependencies": { + "align-text": { + "version": "0.1.4", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" + } + }, + "amdefine": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "bundled": true, + "dev": true + }, + "append-transform": { + "version": "0.4.0", + "bundled": true, + "dev": true, + "requires": { + "default-require-extensions": "1.0.0" + } + }, + "archy": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "arr-diff": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "arr-flatten": "1.1.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "bundled": true, + "dev": true + }, + "arrify": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "async": { + "version": "1.5.2", + "bundled": true, + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, + "babel-generator": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "detect-indent": "4.0.0", + "jsesc": "1.3.0", + "lodash": "4.17.4", + "source-map": "0.5.7", + "trim-right": "1.0.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "bundled": true, + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "core-js": "2.5.3", + "regenerator-runtime": "0.11.1" + } + }, + "babel-template": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "lodash": "4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "2.6.9", + "globals": "9.18.0", + "invariant": "2.2.2", + "lodash": "4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.4", + "to-fast-properties": "1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "bundled": true, + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "1.8.5", + "bundled": true, + "dev": true, + "requires": { + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" + } + }, + "builtin-modules": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "caching-transform": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "md5-hex": "1.3.0", + "mkdirp": "0.5.1", + "write-file-atomic": "1.3.4" + } + }, + "camelcase": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true + }, + "center-align": { + "version": "0.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "align-text": "0.1.4", + "lazy-cache": "1.0.4" + } + }, + "chalk": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "cliui": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "center-align": "0.1.3", + "right-align": "0.1.3", + "wordwrap": "0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "commondir": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "convert-source-map": { + "version": "1.5.1", + "bundled": true, + "dev": true + }, + "core-js": { + "version": "2.5.3", + "bundled": true, + "dev": true + }, + "cross-spawn": { + "version": "4.0.2", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "4.1.1", + "which": "1.3.0" + } + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "debug-log": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "default-require-extensions": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "strip-bom": "2.0.0" + } + }, + "detect-indent": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "repeating": "2.0.1" + } + }, + "error-ex": { + "version": "1.3.1", + "bundled": true, + "dev": true, + "requires": { + "is-arrayish": "0.2.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true, + "dev": true + }, + "esutils": { + "version": "2.0.2", + "bundled": true, + "dev": true + }, + "execa": { + "version": "0.7.0", + "bundled": true, + "dev": true, + "requires": { + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" + } + } + } + }, + "expand-brackets": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "requires": { + "is-posix-bracket": "0.1.1" + } + }, + "expand-range": { + "version": "1.8.2", + "bundled": true, + "dev": true, + "requires": { + "fill-range": "2.2.3" + } + }, + "extglob": { + "version": "0.3.2", + "bundled": true, + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + }, + "filename-regex": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "fill-range": { + "version": "2.2.3", + "bundled": true, + "dev": true, + "requires": { + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.7", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" + } + }, + "find-cache-dir": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "requires": { + "commondir": "1.0.1", + "mkdirp": "0.5.1", + "pkg-dir": "1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "locate-path": "2.0.0" + } + }, + "for-in": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "for-own": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "requires": { + "for-in": "1.0.2" + } + }, + "foreground-child": { + "version": "1.5.6", + "bundled": true, + "dev": true, + "requires": { + "cross-spawn": "4.0.2", + "signal-exit": "3.0.2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "get-caller-file": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "glob-base": { + "version": "0.3.0", + "bundled": true, + "dev": true, + "requires": { + "glob-parent": "2.0.0", + "is-glob": "2.0.1" + } + }, + "glob-parent": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-glob": "2.0.1" + } + }, + "globals": { + "version": "9.18.0", + "bundled": true, + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true, + "dev": true + }, + "handlebars": { + "version": "4.0.11", + "bundled": true, + "dev": true, + "requires": { + "async": "1.5.2", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "bundled": true, + "dev": true, + "requires": { + "amdefine": "1.0.1" + } + } + } + }, + "has-ansi": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "hosted-git-info": { + "version": "2.5.0", + "bundled": true, + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true, + "dev": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "invariant": { + "version": "2.2.2", + "bundled": true, + "dev": true, + "requires": { + "loose-envify": "1.3.1" + } + }, + "invert-kv": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "bundled": true, + "dev": true + }, + "is-buffer": { + "version": "1.1.6", + "bundled": true, + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "builtin-modules": "1.1.1" + } + }, + "is-dotfile": { + "version": "1.0.3", + "bundled": true, + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "bundled": true, + "dev": true, + "requires": { + "is-primitive": "2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "is-extglob": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-glob": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + }, + "is-number": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "bundled": true, + "dev": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "isobject": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "istanbul-lib-coverage": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "istanbul-lib-hook": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "append-transform": "0.4.0" + } + }, + "istanbul-lib-instrument": { + "version": "1.9.1", + "bundled": true, + "dev": true, + "requires": { + "babel-generator": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "istanbul-lib-coverage": "1.1.1", + "semver": "5.4.1" + } + }, + "istanbul-lib-report": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "istanbul-lib-coverage": "1.1.1", + "mkdirp": "0.5.1", + "path-parse": "1.0.5", + "supports-color": "3.2.3" + }, + "dependencies": { + "supports-color": { + "version": "3.2.3", + "bundled": true, + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "1.2.2", + "bundled": true, + "dev": true, + "requires": { + "debug": "3.1.0", + "istanbul-lib-coverage": "1.1.1", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "source-map": "0.5.7" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "istanbul-reports": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "requires": { + "handlebars": "4.0.11" + } + }, + "js-tokens": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "jsesc": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + }, + "lazy-cache": { + "version": "1.0.4", + "bundled": true, + "dev": true, + "optional": true + }, + "lcid": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "invert-kv": "1.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-locate": "2.0.0", + "path-exists": "3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "bundled": true, + "dev": true + } + } + }, + "lodash": { + "version": "4.17.4", + "bundled": true, + "dev": true + }, + "longest": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "loose-envify": { + "version": "1.3.1", + "bundled": true, + "dev": true, + "requires": { + "js-tokens": "3.0.2" + } + }, + "lru-cache": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "md5-hex": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "md5-o-matic": "0.1.1" + } + }, + "md5-o-matic": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "mem": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "mimic-fn": "1.1.0" + } + }, + "merge-source-map": { + "version": "1.0.4", + "bundled": true, + "dev": true, + "requires": { + "source-map": "0.5.7" + } + }, + "micromatch": { + "version": "2.3.11", + "bundled": true, + "dev": true, + "requires": { + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" + } + }, + "mimic-fn": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "normalize-package-data": { + "version": "2.4.0", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "2.5.0", + "is-builtin-module": "1.0.0", + "semver": "5.4.1", + "validate-npm-package-license": "3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "remove-trailing-separator": "1.1.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "path-key": "2.0.1" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true + }, + "object.omit": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "for-own": "0.1.5", + "is-extendable": "0.1.1" + } + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "optimist": { + "version": "0.6.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8", + "wordwrap": "0.0.3" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "os-locale": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" + } + }, + "p-finally": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "p-limit": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "p-locate": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-limit": "1.1.0" + } + }, + "parse-glob": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" + } + }, + "parse-json": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "requires": { + "error-ex": "1.3.1" + } + }, + "path-exists": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "pinkie-promise": "2.0.1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "path-key": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "path-parse": { + "version": "1.0.5", + "bundled": true, + "dev": true + }, + "path-type": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "pify": { + "version": "2.3.0", + "bundled": true, + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "bundled": true, + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "pkg-dir": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "find-up": "1.1.2" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + } + } + }, + "preserve": { + "version": "0.2.0", + "bundled": true, + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "randomatic": { + "version": "1.1.7", + "bundled": true, + "dev": true, + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "read-pkg": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + } + } + }, + "regenerator-runtime": { + "version": "0.11.1", + "bundled": true, + "dev": true + }, + "regex-cache": { + "version": "0.4.4", + "bundled": true, + "dev": true, + "requires": { + "is-equal-shallow": "0.1.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "repeat-element": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "bundled": true, + "dev": true + }, + "repeating": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-finite": "1.0.2" + } + }, + "require-directory": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "resolve-from": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "right-align": { + "version": "0.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "align-text": "0.1.4" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "semver": { + "version": "5.4.1", + "bundled": true, + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "shebang-regex": "1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "slide": { + "version": "1.1.6", + "bundled": true, + "dev": true + }, + "source-map": { + "version": "0.5.7", + "bundled": true, + "dev": true + }, + "spawn-wrap": { + "version": "1.4.2", + "bundled": true, + "dev": true, + "requires": { + "foreground-child": "1.5.6", + "mkdirp": "0.5.1", + "os-homedir": "1.0.2", + "rimraf": "2.6.2", + "signal-exit": "3.0.2", + "which": "1.3.0" + } + }, + "spdx-correct": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "spdx-license-ids": "1.2.2" + } + }, + "spdx-expression-parse": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "spdx-license-ids": { + "version": "1.2.2", + "bundled": true, + "dev": true + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "test-exclude": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "requires": { + "arrify": "1.0.1", + "micromatch": "2.3.11", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "require-main-filename": "1.0.1" + } + }, + "to-fast-properties": { + "version": "1.0.3", + "bundled": true, + "dev": true + }, + "trim-right": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" + }, + "dependencies": { + "yargs": { + "version": "3.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", + "window-size": "0.1.0" + } + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "validate-npm-package-license": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "spdx-correct": "1.0.2", + "spdx-expression-parse": "1.0.4" + } + }, + "which": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "isexe": "2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "window-size": { + "version": "0.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "wordwrap": { + "version": "0.0.3", + "bundled": true, + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "write-file-atomic": { + "version": "1.3.4", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "slide": "1.1.6" + } + }, + "y18n": { + "version": "3.2.1", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "2.1.2", + "bundled": true, + "dev": true + }, + "yargs": { + "version": "10.0.3", + "bundled": true, + "dev": true, + "requires": { + "cliui": "3.2.0", + "decamelize": "1.2.0", + "find-up": "2.1.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "8.0.0" + }, + "dependencies": { + "cliui": { + "version": "3.2.0", + "bundled": true, + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + } + } + } + } + }, + "yargs-parser": { + "version": "8.0.0", + "bundled": true, + "dev": true, + "requires": { + "camelcase": "4.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "bundled": true, + "dev": true + } + } + } + } + }, "oauth-sign": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", diff --git a/package.json b/package.json index 94030401..0f10f77b 100644 --- a/package.json +++ b/package.json @@ -35,10 +35,13 @@ "@types/node": "^9.4.6", "browserify": "^16.1.1", "chai": "^4.1.2", + "coveralls": "^3.0.0", "firebase": "4.x.x", "jsdom": "^11.6.2", "jsdom-global": "^3.0.2", "mocha": "^5.0.4", + "nyc": "^11.4.1", + "source-map-support": "^0.5.3", "ts-node": "^5.0.1", "tslint": "^5.9.1", "typescript": "^2.7.2", @@ -47,8 +50,22 @@ "scripts": { "browserify": "browserify dist/firebase/index.js -o dist/browser/geofire.js && browserify dist/firestore/index.js -o dist/browser/geofirestore.js", "build": "rm -rf ./dist && tsc && npm run browserify && npm run uglify", - "test": "mocha --reporter spec -r ts-node/register -r jsdom-global/register --timeout 20000 --exit 'tests/**/*.test.ts'", + "coverage": "nyc report --reporter=text-lcov | coveralls", + "test": "nyc --reporter=html --reporter=text mocha", "travis": "npm run test && npm run build", "uglify": "uglifyjs dist/browser/geofire.js -c -m -o dist/browser/geofire.min.js && uglifyjs dist/browser/geofirestore.js -c -m -o dist/browser/geofirestore.min.js" + }, + "nyc": { + "extension": [ + ".ts" + ], + "exclude": [ + "**/*.d.ts", + "**/*.js" + ], + "reporter": [ + "html" + ], + "all": true } } diff --git a/tests/common.ts b/test/common.ts similarity index 96% rename from tests/common.ts rename to test/common.ts index 9e074d1d..9a43d048 100644 --- a/tests/common.ts +++ b/test/common.ts @@ -31,9 +31,12 @@ export let geoFirestoreRef: firebase.firestore.CollectionReference, // Initialize Firebase const config = { - apiKey: 'AIzaSyDPmGjw0Igt75VpzGmfgdqmHornPneV7zQ', - databaseURL: 'https://geofire-5f3fc.firebaseio.com', - projectId: 'geofire-5f3fc', + apiKey: 'AIzaSyDyHvs6rPWhlKF7kBi6OIYFSf8EpFhx4i4', + databaseURL: 'https://geofire-4c79d.firebaseio.com', + projectId: 'geofire-4c79d' + // apiKey: 'AIzaSyC5IcRccDo289TTRa3Y7qJIu8YPz3EnKAI', + // databaseURL: 'https://geofire-9d0de.firebaseio.com', + // projectId: 'geofire-9d0de' }; firebase.initializeApp(config); diff --git a/tests/firebase/index.test.ts b/test/firebase/index.test.ts similarity index 100% rename from tests/firebase/index.test.ts rename to test/firebase/index.test.ts diff --git a/tests/firebase/query.test.ts b/test/firebase/query.test.ts similarity index 100% rename from tests/firebase/query.test.ts rename to test/firebase/query.test.ts diff --git a/tests/firestore/index.test.ts b/test/firestore/index.test.ts similarity index 100% rename from tests/firestore/index.test.ts rename to test/firestore/index.test.ts diff --git a/tests/firestore/query.test.ts b/test/firestore/query.test.ts similarity index 100% rename from tests/firestore/query.test.ts rename to test/firestore/query.test.ts diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 00000000..6f23bd37 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,6 @@ +--reporter spec +--require ts-node/register +--require source-map-support/register +--require jsdom-global/register +--timeout 15000 +--exit test/**/*.test.ts \ No newline at end of file diff --git a/tests/tools/geoFireCallbackRegistration.test.ts b/test/tools/geoFireCallbackRegistration.test.ts similarity index 100% rename from tests/tools/geoFireCallbackRegistration.test.ts rename to test/tools/geoFireCallbackRegistration.test.ts diff --git a/tests/tools/geoFirestoreCallbackRegistration.test.ts b/test/tools/geoFirestoreCallbackRegistration.test.ts similarity index 98% rename from tests/tools/geoFirestoreCallbackRegistration.test.ts rename to test/tools/geoFirestoreCallbackRegistration.test.ts index 45ca14a6..bd7a25df 100644 --- a/tests/tools/geoFirestoreCallbackRegistration.test.ts +++ b/test/tools/geoFirestoreCallbackRegistration.test.ts @@ -121,14 +121,15 @@ describe('GeoFirestore GeoCallbackRegistration Tests:', () => { onKeyExitedRegistration.cancel(); cl.x('p3'); - return wait(100).then(() => geoFirestore.set('loc3', [-80, -80])); + return geoFirestore.set('loc3', [-80, -80]); }).then(() => { cl.x('p4'); return wait(100); }).then(() => { cl.x('p5'); - }).catch(failTestOnCaughtError); + }) + .catch(failTestOnCaughtError); }); it('Cancelling a \'key_moved\' registration does not cancel all \'key_moved\' callbacks', (done) => { diff --git a/tests/tools/utils.test.ts b/test/tools/utils.test.ts similarity index 100% rename from tests/tools/utils.test.ts rename to test/tools/utils.test.ts diff --git a/tsconfig.json b/tsconfig.json index 6eb2e8f3..e64181e0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "outDir": "./dist", "baseUrl": ".", - "sourceMap": false, + "inlineSourceMap": true, "declaration": true, "moduleResolution": "node", "emitDecoratorMetadata": true, From aa7b084eb6020e6169576f99baf6bf1c2486d3bf Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Sun, 11 Mar 2018 02:57:17 -0400 Subject: [PATCH 14/23] test: increase mocha timeout --- test/common.ts | 6 +++--- test/firestore/index.test.ts | 6 +++--- test/mocha.opts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/common.ts b/test/common.ts index 9a43d048..67635c1b 100644 --- a/test/common.ts +++ b/test/common.ts @@ -31,9 +31,9 @@ export let geoFirestoreRef: firebase.firestore.CollectionReference, // Initialize Firebase const config = { - apiKey: 'AIzaSyDyHvs6rPWhlKF7kBi6OIYFSf8EpFhx4i4', - databaseURL: 'https://geofire-4c79d.firebaseio.com', - projectId: 'geofire-4c79d' + apiKey: 'AIzaSyDfEvcG1WHlI8o_KKK2yQcTHkz63_8nZK8', + databaseURL: 'https://geofi-5653f.firebaseio.com', + projectId: 'geofi-5653f' // apiKey: 'AIzaSyC5IcRccDo289TTRa3Y7qJIu8YPz3EnKAI', // databaseURL: 'https://geofire-9d0de.firebaseio.com', // projectId: 'geofire-9d0de' diff --git a/test/firestore/index.test.ts b/test/firestore/index.test.ts index 2c5c7267..f61e1df2 100755 --- a/test/firestore/index.test.ts +++ b/test/firestore/index.test.ts @@ -20,20 +20,20 @@ describe('GeoFirestore Tests:', () => { }); describe('Constructor:', () => { - it('Constructor throws errors given invalid Firebase references', () => { + it('Constructor throws errors given invalid Firestore Collection references', () => { invalidFirebaseRefs.forEach((invalidFirebaseRef) => { // @ts-ignore expect(() => new GeoFirestore(invalidFirebaseRef)).to.throw(null, 'collectionRef must be an instance of a Firestore Collection'); }); }); - it('Constructor does not throw errors given valid Firebase references', () => { + it('Constructor does not throw errors given valid Firestore Collection references', () => { expect(() => new GeoFirestore(geoFirestoreRef)).not.to.throw(); }); }); describe('ref():', () => { - it('ref() returns the Firebase reference used to create a GeoFire instance', () => { + it('ref() returns the Firestore Collection reference used to create a GeoFire instance', () => { expect(geoFirestore.ref()).to.deep.equal(geoFirestoreRef); }); }); diff --git a/test/mocha.opts b/test/mocha.opts index 6f23bd37..c0dacf2f 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -2,5 +2,5 @@ --require ts-node/register --require source-map-support/register --require jsdom-global/register ---timeout 15000 +--timeout 30000 --exit test/**/*.test.ts \ No newline at end of file From b5da5ee427e5665a0afd76c8e119e027b39dc10d Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Sun, 11 Mar 2018 03:20:33 -0400 Subject: [PATCH 15/23] test(firestore): modify "'key_exited' registrations can be cancelled" timing --- test/tools/geoFirestoreCallbackRegistration.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/tools/geoFirestoreCallbackRegistration.test.ts b/test/tools/geoFirestoreCallbackRegistration.test.ts index bd7a25df..cc08f316 100644 --- a/test/tools/geoFirestoreCallbackRegistration.test.ts +++ b/test/tools/geoFirestoreCallbackRegistration.test.ts @@ -103,6 +103,7 @@ describe('GeoFirestore GeoCallbackRegistration Tests:', () => { const onKeyExitedRegistration = geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); + onKeyExitedRegistration.cancel(); }); geoFirestore.set({ @@ -118,7 +119,6 @@ describe('GeoFirestore GeoCallbackRegistration Tests:', () => { return wait(100); }).then(() => { - onKeyExitedRegistration.cancel(); cl.x('p3'); return geoFirestore.set('loc3', [-80, -80]); @@ -128,8 +128,7 @@ describe('GeoFirestore GeoCallbackRegistration Tests:', () => { return wait(100); }).then(() => { cl.x('p5'); - }) - .catch(failTestOnCaughtError); + }).catch(failTestOnCaughtError); }); it('Cancelling a \'key_moved\' registration does not cancel all \'key_moved\' callbacks', (done) => { From ffd9a6026ea569fde6137f2ce830e94766dfbcb5 Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Tue, 10 Apr 2018 11:11:08 -0400 Subject: [PATCH 16/23] chore: remove firestore support --- package.json | 6 +- src/firestore/index.ts | 160 -- src/firestore/query.ts | 535 ------- .../geoCallbackRegistration.ts} | 0 .../utils.ts => geoFire/geoFireUtils.ts} | 2 +- .../query.ts => geoFire/geoQuery.ts} | 12 +- src/{firebase => geoFire}/index.ts | 16 +- src/{ => geoFire}/interfaces/geoFireObj.ts | 0 src/{ => geoFire}/interfaces/index.ts | 1 - src/{ => geoFire}/interfaces/queryCriteria.ts | 0 src/index.ts | 5 +- src/interfaces/geoFirestoreQueryState.ts | 5 - test/common.ts | 93 +- test/firestore/index.test.ts | 848 ---------- test/firestore/query.test.ts | 1420 ----------------- .../geoCallbackRegistration.test.ts} | 38 +- .../index.test.ts => tests/geoFire.test.ts} | 12 +- .../geoFireUtils.test.ts} | 4 +- .../query.test.ts => tests/geoQuery.test.ts} | 358 ++--- .../geoFirestoreCallbackRegistration.test.ts | 253 --- 20 files changed, 237 insertions(+), 3531 deletions(-) delete mode 100644 src/firestore/index.ts delete mode 100644 src/firestore/query.ts rename src/{tools/callbackRegistration.ts => geoFire/geoCallbackRegistration.ts} (100%) rename src/{tools/utils.ts => geoFire/geoFireUtils.ts} (99%) rename src/{firebase/query.ts => geoFire/geoQuery.ts} (98%) rename src/{firebase => geoFire}/index.ts (91%) rename src/{ => geoFire}/interfaces/geoFireObj.ts (100%) rename src/{ => geoFire}/interfaces/index.ts (59%) rename src/{ => geoFire}/interfaces/queryCriteria.ts (100%) delete mode 100644 src/interfaces/geoFirestoreQueryState.ts delete mode 100755 test/firestore/index.test.ts delete mode 100644 test/firestore/query.test.ts rename test/{tools/geoFireCallbackRegistration.test.ts => tests/geoCallbackRegistration.test.ts} (76%) rename test/{firebase/index.test.ts => tests/geoFire.test.ts} (98%) rename test/{tools/utils.test.ts => tests/geoFireUtils.test.ts} (99%) rename test/{firebase/query.test.ts => tests/geoQuery.test.ts} (70%) delete mode 100644 test/tools/geoFirestoreCallbackRegistration.test.ts diff --git a/package.json b/package.json index 0f10f77b..13e60822 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "package.json" ], "peerDependencies": { - "firebase": "^4.x.x" + "firebase": "^2.4.0 || 3.x.x || 4.x.x" }, "devDependencies": { "@types/chai": "^4.1.2", @@ -48,12 +48,12 @@ "uglify-js": "^3.3.13" }, "scripts": { - "browserify": "browserify dist/firebase/index.js -o dist/browser/geofire.js && browserify dist/firestore/index.js -o dist/browser/geofirestore.js", + "browserify": "browserify dist/geoFire/index.js -o dist/browser/geofire.js", "build": "rm -rf ./dist && tsc && npm run browserify && npm run uglify", "coverage": "nyc report --reporter=text-lcov | coveralls", "test": "nyc --reporter=html --reporter=text mocha", "travis": "npm run test && npm run build", - "uglify": "uglifyjs dist/browser/geofire.js -c -m -o dist/browser/geofire.min.js && uglifyjs dist/browser/geofirestore.js -c -m -o dist/browser/geofirestore.min.js" + "uglify": "uglifyjs dist/browser/geofire.js -c -m -o dist/browser/geofire.min.js" }, "nyc": { "extension": [ diff --git a/src/firestore/index.ts b/src/firestore/index.ts deleted file mode 100644 index b1c357d4..00000000 --- a/src/firestore/index.ts +++ /dev/null @@ -1,160 +0,0 @@ -/*! - * GeoFire is an open-source library that allows you to store and query a set - * of keys based on their geographic location. At its heart, GeoFire simply - * stores locations with string keys. Its main benefit, however, is the - * possibility of retrieving only those keys within a given geographic area - - * all in realtime. - * - * GeoFire 0.0.0 - * https://github.com/firebase/geofire-js/ - * License: MIT - */ - -import * as firebase from 'firebase'; - -import { GeoFirestoreQuery } from './query'; -import { decodeGeoFireObject, degreesToRadians, encodeGeoFireObject, encodeGeohash, validateLocation, validateKey } from '../tools/utils'; - -import { QueryCriteria, GeoFireObj } from '../interfaces'; - -/** - * Creates a GeoFirestore instance. - */ -export class GeoFirestore { - /** - * @param _collectionRef A Firestore Collection reference where the GeoFirestore data will be stored. - */ - constructor(private _collectionRef: firebase.firestore.CollectionReference) { - if (Object.prototype.toString.call(this._collectionRef) !== '[object Object]') { - throw new Error('collectionRef must be an instance of a Firestore Collection'); - } - } - - /********************/ - /* PUBLIC METHODS */ - /********************/ - /** - * Returns a promise fulfilled with the location corresponding to the provided key. - * - * If the provided key does not exist, the returned promise is fulfilled with null. - * - * @param key The key of the location to retrieve. - * @returns A promise that is fulfilled with the location of the given key. - */ - public get(key: string): Promise { - validateKey(key); - return this._collectionRef.doc(key).get().then((documentSnapshot: firebase.firestore.DocumentSnapshot) => { - if (!documentSnapshot.exists) { - return null; - } else { - const snapshotVal = documentSnapshot.data(); - return decodeGeoFireObject(snapshotVal); - } - }); - }; - - /** - * Returns the Firestore Collection used to create this GeoFirestore instance. - * - * @returns The Firestore Collection used to create this GeoFirestore instance. - */ - public ref(): firebase.firestore.CollectionReference { - return this._collectionRef; - }; - - /** - * Removes the provided key from this GeoFirestore. Returns an empty promise fulfilled when the key has been removed. - * - * If the provided key is not in this GeoFirestore, the promise will still successfully resolve. - * - * @param key The key of the location to remove. - * @returns A promise that is fulfilled after the inputted key is removed. - */ - public remove(key: string): Promise { - return this.set(key, null); - }; - - /** - * Adds the provided key - location pair(s) to Firestore. Returns an empty promise which is fulfilled when the write is complete. - * - * If any provided key already exists in this GeoFirestore, it will be overwritten with the new location value. - * - * @param keyOrLocations The key representing the location to add or a mapping of key - location pairs which - * represent the locations to add. - * @param location The [latitude, longitude] pair to add. - * @returns A promise that is fulfilled when the write is complete. - */ - public set(keyOrLocations: string | any, location?: number[]): Promise { - if (typeof keyOrLocations === 'string' && keyOrLocations.length !== 0) { - validateKey(keyOrLocations); - if (location === null) { - // Setting location to null is valid since it will remove the key - return this._collectionRef.doc(keyOrLocations).delete(); - } else { - validateLocation(location); - const geohash: string = encodeGeohash(location); - return this._collectionRef.doc(keyOrLocations).set(encodeGeoFireObject(location, geohash)); - } - } else if (typeof keyOrLocations === 'object') { - if (typeof location !== 'undefined') { - throw new Error('The location argument should not be used if you pass an object to set().'); - } - } else { - throw new Error('keyOrLocations must be a string or a mapping of key - location pairs.'); - } - - const batch: firebase.firestore.WriteBatch = this._collectionRef.firestore.batch(); - Object.keys(keyOrLocations).forEach((key) => { - validateKey(key); - const ref = this._collectionRef.doc(key); - const location: number[] = keyOrLocations[key]; - if (location === null) { - batch.delete(ref); - } else { - validateLocation(location); - const geohash: string = encodeGeohash(location); - batch.set(ref, encodeGeoFireObject(location, geohash), { merge: true }); - } - }); - return batch.commit(); - }; - - /** - * Returns a new GeoQuery instance with the provided queryCriteria. - * - * @param queryCriteria The criteria which specifies the GeoQuery's center and radius. - * @return A new GeoFirestoreQuery object. - */ - public query(queryCriteria: QueryCriteria): GeoFirestoreQuery { - return new GeoFirestoreQuery(this._collectionRef, queryCriteria); - }; - - /********************/ - /* STATIC METHODS */ - /********************/ - /** - * Static method which calculates the distance, in kilometers, between two locations, - * via the Haversine formula. Note that this is approximate due to the fact that the - * Earth's radius varies between 6356.752 km and 6378.137 km. - * - * @param location1 The [latitude, longitude] pair of the first location. - * @param location2 The [latitude, longitude] pair of the second location. - * @returns The distance, in kilometers, between the inputted locations. - */ - static distance(location1: number[], location2: number[]) { - validateLocation(location1); - validateLocation(location2); - - var radius = 6371; // Earth's radius in kilometers - var latDelta = degreesToRadians(location2[0] - location1[0]); - var lonDelta = degreesToRadians(location2[1] - location1[1]); - - var a = (Math.sin(latDelta / 2) * Math.sin(latDelta / 2)) + - (Math.cos(degreesToRadians(location1[0])) * Math.cos(degreesToRadians(location2[0])) * - Math.sin(lonDelta / 2) * Math.sin(lonDelta / 2)); - - var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - - return radius * c; - }; -} diff --git a/src/firestore/query.ts b/src/firestore/query.ts deleted file mode 100644 index efed2c0a..00000000 --- a/src/firestore/query.ts +++ /dev/null @@ -1,535 +0,0 @@ -import * as firebase from 'firebase'; - -import { GeoFirestore } from './'; -import { GeoCallbackRegistration } from '../tools/callbackRegistration'; -import { decodeGeoFireObject, encodeGeohash, geoFirestoreGetKey, geohashQueries, validateCriteria, validateLocation } from '../tools/utils'; - -import { QueryCriteria, GeoFireObj, GeoFirestoreQueryState } from '../interfaces'; - -/** - * Creates a GeoFirestoreQuery instance. - */ -export class GeoFirestoreQuery { - // Event callbacks - private _callbacks: any = { ready: [], key_entered: [], key_exited: [], key_moved: [] }; - // Variable to track when the query is cancelled - private _cancelled: boolean = false; - private _center: number[]; - // A dictionary of geohash queries which currently have an active callbacks - private _currentGeohashesQueried: any = {}; - // A dictionary of locations that a currently active in the queries - // Note that not all of these are currently within this query - private _locationsTracked: any = {}; - private _radius: number; - - // Variables used to keep track of when to fire the 'ready' event - private _valueEventFired: boolean = false; - private _outstandingGeohashReadyEvents: any; - // Every ten seconds, clean up the geohashes we are currently querying for. We keep these around - // for a little while since it's likely that they will need to be re-queried shortly after they - // move outside of the query's bounding box. - private _geohashCleanupScheduled: boolean = false; - private _cleanUpCurrentGeohashesQueriedInterval: NodeJS.Timer; - private _cleanUpCurrentGeohashesQueriedTimeout = null; - - /** - * @param _collectionRef A Firestore Collection reference where the GeoFirestore data will be stored. - * @param _queryCriteria The criteria which specifies the query's center and radius. - */ - constructor(private _collectionRef: firebase.firestore.CollectionReference, private _queryCriteria: QueryCriteria) { - // Firebase reference of the GeoFirestore which created this query - if (Object.prototype.toString.call(this._collectionRef) !== '[object Object]') { - throw new Error('firebaseRef must be an instance of Firestore'); - } - - this._cleanUpCurrentGeohashesQueriedInterval = setInterval(() => { - if (this._geohashCleanupScheduled === false) { - this._cleanUpCurrentGeohashesQueried(); - } - }, 10000); - - // Validate and save the query criteria - validateCriteria(_queryCriteria, true); - this._center = _queryCriteria.center; - this._radius = _queryCriteria.radius; - - // Listen for new geohashes being added around this query and fire the appropriate events - this._listenForNewGeohashes(); - } - - /********************/ - /* PUBLIC METHODS */ - /********************/ - /** - * Terminates this query so that it no longer sends location updates. All callbacks attached to this - * query via on() will be cancelled. This query can no longer be used in the future. - */ - public cancel(): void { - // Mark this query as cancelled - this._cancelled = true; - - // Cancel all callbacks in this query's callback list - this._callbacks = { ready: [], key_entered: [], key_exited: [], key_moved: [] }; - - // Turn off all Firebase listeners for the current geohashes being queried - const keys: string[] = Object.keys(this._currentGeohashesQueried); - keys.forEach((geohashQueryStr: string) => { - const query: string[] = this._stringToQuery(geohashQueryStr); - this._cancelGeohashQuery(query, this._currentGeohashesQueried[geohashQueryStr]); - delete this._currentGeohashesQueried[geohashQueryStr]; - }); - - // Delete any stored locations - this._locationsTracked = {}; - - // Turn off the current geohashes queried clean up interval - clearInterval(this._cleanUpCurrentGeohashesQueriedInterval); - }; - - /** - * Returns the location signifying the center of this query. - * - * @returns The [latitude, longitude] pair signifying the center of this query. - */ - public center(): number[] { - return this._center; - }; - - /** - * Attaches a callback to this query which will be run when the provided eventType fires. Valid eventType - * values are 'ready', 'key_entered', 'key_exited', and 'key_moved'. The ready event callback is passed no - * parameters. All other callbacks will be passed three parameters: (1) the location's key, (2) the location's - * [latitude, longitude] pair, and (3) the distance, in kilometers, from the location to this query's center - * - * 'ready' is used to signify that this query has loaded its initial state and is up-to-date with its corresponding - * GeoFirestore instance. 'ready' fires when this query has loaded all of the initial data from GeoFirestore and fired all - * other events for that data. It also fires every time updateCriteria() is called, after all other events have - * fired for the updated query. - * - * 'key_entered' fires when a key enters this query. This can happen when a key moves from a location outside of - * this query to one inside of it or when a key is written to GeoFirestore for the first time and it falls within - * this query. - * - * 'key_exited' fires when a key moves from a location inside of this query to one outside of it. If the key was - * entirely removed from GeoFire, both the location and distance passed to the callback will be null. - * - * 'key_moved' fires when a key which is already in this query moves to another location inside of it. - * - * Returns a GeoCallbackRegistration which can be used to cancel the callback. You can add as many callbacks - * as you would like for the same eventType by repeatedly calling on(). Each one will get called when its - * corresponding eventType fires. Each callback must be cancelled individually. - * - * @param eventType The event type for which to attach the callback. One of 'ready', 'key_entered', - * 'key_exited', or 'key_moved'. - * @param callback Callback function to be called when an event of type eventType fires. - * @returns A callback registration which can be used to cancel the provided callback. - */ - public on(eventType: string, callback: Function): GeoCallbackRegistration { - // Validate the inputs - if (['ready', 'key_entered', 'key_exited', 'key_moved'].indexOf(eventType) === -1) { - throw new Error('event type must be \'ready\', \'key_entered\', \'key_exited\', or \'key_moved\''); - } - if (typeof callback !== 'function') { - throw new Error('callback must be a function'); - } - - // Add the callback to this query's callbacks list - this._callbacks[eventType].push(callback); - - // If this is a 'key_entered' callback, fire it for every location already within this query - if (eventType === 'key_entered') { - const keys: string[] = Object.keys(this._locationsTracked); - keys.forEach((key: string) => { - const locationDict = this._locationsTracked[key]; - if (typeof locationDict !== 'undefined' && locationDict.isInQuery) { - callback(key, locationDict.location, locationDict.distanceFromCenter); - } - }); - } - - // If this is a 'ready' callback, fire it if this query is already ready - if (eventType === 'ready' && this._valueEventFired) { - callback(); - } - - // Return an event registration which can be used to cancel the callback - return new GeoCallbackRegistration(() => { - this._callbacks[eventType].splice(this._callbacks[eventType].indexOf(callback), 1); - }); - }; - - /** - * Returns the radius of this query, in kilometers. - * - * @returns The radius of this query, in kilometers. - */ - public radius(): number { - return this._radius; - }; - - /** - * Updates the criteria for this query. - * - * @param newQueryCriteria The criteria which specifies the query's center and radius. - */ - public updateCriteria(newQueryCriteria: QueryCriteria): void { - // Validate and save the new query criteria - validateCriteria(newQueryCriteria); - this._center = newQueryCriteria.center || this._center; - this._radius = newQueryCriteria.radius || this._radius; - - // Loop through all of the locations in the query, update their distance from the center of the - // query, and fire any appropriate events - const keys: string[] = Object.keys(this._locationsTracked); - for (const key of keys) { - // If the query was cancelled while going through this loop, stop updating locations and stop - // firing events - if (this._cancelled === true) { - break; - } - // Get the cached information for this location - const locationDict = this._locationsTracked[key]; - // Save if the location was already in the query - const wasAlreadyInQuery = locationDict.isInQuery; - // Update the location's distance to the new query center - locationDict.distanceFromCenter = GeoFirestore.distance(locationDict.location, this._center); - // Determine if the location is now in this query - locationDict.isInQuery = (locationDict.distanceFromCenter <= this._radius); - // If the location just left the query, fire the 'key_exited' callbacks - // Else if the location just entered the query, fire the 'key_entered' callbacks - if (wasAlreadyInQuery && !locationDict.isInQuery) { - this._fireCallbacksForKey('key_exited', key, locationDict.location, locationDict.distanceFromCenter); - } else if (!wasAlreadyInQuery && locationDict.isInQuery) { - this._fireCallbacksForKey('key_entered', key, locationDict.location, locationDict.distanceFromCenter); - } - } - - // Reset the variables which control when the 'ready' event fires - this._valueEventFired = false; - - // Listen for new geohashes being added to GeoFirestore and fire the appropriate events - this._listenForNewGeohashes(); - }; - - - /*********************/ - /* PRIVATE METHODS */ - /*********************/ - /** - * Turns off all callbacks for the provide geohash query. - * - * @param query The geohash query. - * @param queryState An object storing the current state of the query. - */ - private _cancelGeohashQuery(query: string[], queryState: GeoFirestoreQueryState): void { - queryState.childCallback(); - queryState.valueCallback(); - } - - /** - * Callback for child added events. - * - * @param locationDataSnapshot A snapshot of the data stored for this location. - */ - private _childAddedCallback(locationDataSnapshot: firebase.firestore.DocumentSnapshot): void { - const data = locationDataSnapshot.data(); - this._updateLocation(geoFirestoreGetKey(locationDataSnapshot), decodeGeoFireObject(data)); - } - - /** - * Callback for child changed events - * - * @param locationDataSnapshot A snapshot of the data stored for this location. - */ - private _childChangedCallback(locationDataSnapshot: firebase.firestore.DocumentSnapshot): void { - const data = locationDataSnapshot.data(); - this._updateLocation(geoFirestoreGetKey(locationDataSnapshot), decodeGeoFireObject(data)); - } - - /** - * Callback for child removed events - * - * @param locationDataSnapshot A snapshot of the data stored for this location. - */ - private _childRemovedCallback(locationDataSnapshot: firebase.firestore.DocumentSnapshot): void { - const key: string = geoFirestoreGetKey(locationDataSnapshot); - if (key in this._locationsTracked) { - this._collectionRef.doc(key).get().then((snapshot: firebase.firestore.DocumentSnapshot) => { - const data = (!snapshot.exists) ? null : snapshot.data(); - const location: number[] = (!snapshot.exists) ? null : decodeGeoFireObject(data); - const geohash: string = (location !== null) ? encodeGeohash(location) : null; - // Only notify observers if key is not part of any other geohash query or this actually might not be - // a key exited event, but a key moved or entered event. These events will be triggered by updates - // to a different query - if (!this._geohashInSomeQuery(geohash)) { - this._removeLocation(key, location); - } - }); - } - } - - /** - * Removes unnecessary Firebase queries which are currently being queried. - */ - private _cleanUpCurrentGeohashesQueried(): void { - let keys: string[] = Object.keys(this._currentGeohashesQueried); - keys.forEach((geohashQueryStr: string) => { - const queryState: any = this._currentGeohashesQueried[geohashQueryStr]; - if (queryState.active === false) { - const query = this._stringToQuery(geohashQueryStr); - // Delete the geohash since it should no longer be queried - this._cancelGeohashQuery(query, queryState); - delete this._currentGeohashesQueried[geohashQueryStr]; - } - }); - - // Delete each location which should no longer be queried - keys = Object.keys(this._locationsTracked); - keys.forEach((key: string) => { - if (!this._geohashInSomeQuery(this._locationsTracked[key].geohash)) { - if (this._locationsTracked[key].isInQuery) { - throw new Error('Internal State error, trying to remove location that is still in query'); - } - delete this._locationsTracked[key]; - } - }); - - // Specify that this is done cleaning up the current geohashes queried - this._geohashCleanupScheduled = false; - - // Cancel any outstanding scheduled cleanup - if (this._cleanUpCurrentGeohashesQueriedTimeout !== null) { - clearTimeout(this._cleanUpCurrentGeohashesQueriedTimeout); - this._cleanUpCurrentGeohashesQueriedTimeout = null; - } - } - - /** - * Fires each callback for the provided eventType, passing it provided key's data. - * - * @param eventType The event type whose callbacks to fire. One of 'key_entered', 'key_exited', or 'key_moved'. - * @param key The key of the location for which to fire the callbacks. - * @param location The location as [latitude, longitude] pair - * @param distanceFromCenter The distance from the center or null. - */ - private _fireCallbacksForKey(eventType: string, key: string, location?: number[], distanceFromCenter?: number): void { - this._callbacks[eventType].forEach((callback) => { - if (typeof location === 'undefined' || location === null) { - callback(key, null, null); - } else { - callback(key, location, distanceFromCenter); - } - }); - } - - /** - * Fires each callback for the 'ready' event. - */ - private _fireReadyEventCallbacks(): void { - this._callbacks.ready.forEach((callback) => { - callback(); - }); - } - - /** - * Checks if this geohash is currently part of any of the geohash queries. - * - * @param geohash The geohash. - * @returns Returns true if the geohash is part of any of the current geohash queries. - */ - private _geohashInSomeQuery(geohash: string): boolean { - const keys: string[] = Object.keys(this._currentGeohashesQueried); - for (const queryStr of keys) { - if (queryStr in this._currentGeohashesQueried) { - const query = this._stringToQuery(queryStr); - if (geohash >= query[0] && geohash <= query[1]) { - return true; - } - } - } - - return false; - } - - /** - * Called once all geohash queries have received all child added events and fires the ready - * event if necessary. - */ - private _geohashQueryReadyCallback(queryStr?: string): void { - const index: number = this._outstandingGeohashReadyEvents.indexOf(queryStr); - if (index > -1) { - this._outstandingGeohashReadyEvents.splice(index, 1); - } - this._valueEventFired = (this._outstandingGeohashReadyEvents.length === 0); - - // If all queries have been processed, fire the ready event - if (this._valueEventFired) { - this._fireReadyEventCallbacks(); - } - } - - /** - * Attaches listeners to Firebase which track when new geohashes are added within this query's - * bounding box. - */ - private _listenForNewGeohashes(): void { - // Get the list of geohashes to query - let geohashesToQuery: string[] = geohashQueries(this._center, this._radius * 1000).map(this._queryToString); - - // Filter out duplicate geohashes - geohashesToQuery = geohashesToQuery.filter((geohash: string, i: number) => geohashesToQuery.indexOf(geohash) === i); - - // For all of the geohashes that we are already currently querying, check if they are still - // supposed to be queried. If so, don't re-query them. Otherwise, mark them to be un-queried - // next time we clean up the current geohashes queried dictionary. - const keys: string[] = Object.keys(this._currentGeohashesQueried); - keys.forEach((geohashQueryStr: string) => { - const index: number = geohashesToQuery.indexOf(geohashQueryStr); - if (index === -1) { - this._currentGeohashesQueried[geohashQueryStr].active = false; - } else { - this._currentGeohashesQueried[geohashQueryStr].active = true; - geohashesToQuery.splice(index, 1); - } - }); - - // If we are not already cleaning up the current geohashes queried and we have more than 25 of them, - // kick off a timeout to clean them up so we don't create an infinite number of unneeded queries. - if (this._geohashCleanupScheduled === false && Object.keys(this._currentGeohashesQueried).length > 25) { - this._geohashCleanupScheduled = true; - this._cleanUpCurrentGeohashesQueriedTimeout = setTimeout(this._cleanUpCurrentGeohashesQueried, 10); - } - - // Keep track of which geohashes have been processed so we know when to fire the 'ready' event - this._outstandingGeohashReadyEvents = geohashesToQuery.slice(); - - // Loop through each geohash to query for and listen for new geohashes which have the same prefix. - // For every match, attach a value callback which will fire the appropriate events. - // Once every geohash to query is processed, fire the 'ready' event. - geohashesToQuery.forEach((toQueryStr: string) => { - // decode the geohash query string - const query: string[] = this._stringToQuery(toQueryStr); - - // Create the Firebase query - const firestoreQuery: firebase.firestore.Query = this._collectionRef.orderBy('g').startAt(query[0]).endAt(query[1]); - - // For every new matching geohash, determine if we should fire the 'key_entered' event - const childCallback = firestoreQuery.onSnapshot((snapshot: firebase.firestore.QuerySnapshot) => { - snapshot.docChanges.forEach((change: firebase.firestore.DocumentChange) => { - if (change.type === 'added') { - this._childAddedCallback(change.doc); - } - if (change.type === 'modified') { - this._childChangedCallback(change.doc); - } - if (change.type === 'removed') { - this._childRemovedCallback(change.doc); - } - }); - }); - - // Once the current geohash to query is processed, see if it is the last one to be processed - // and, if so, mark the value event as fired. - // Note that Firebase fires the 'value' event after every 'added' event fires. - const valueCallback = firestoreQuery.onSnapshot(() => { - valueCallback(); - this._geohashQueryReadyCallback(toQueryStr); - }); - - // Add the geohash query to the current geohashes queried dictionary and save its state - this._currentGeohashesQueried[toQueryStr] = { - active: true, - childCallback: childCallback, - valueCallback: valueCallback - }; - }); - // Based upon the algorithm to calculate geohashes, it's possible that no 'new' - // geohashes were queried even if the client updates the radius of the query. - // This results in no 'READY' event being fired after the .updateCriteria() call. - // Check to see if this is the case, and trigger the 'READY' event. - if (geohashesToQuery.length === 0) { - this._geohashQueryReadyCallback(); - } - } - - /** - * Encodes a query as a string for easier indexing and equality. - * - * @param query The query to encode. - * @returns The encoded query as string. - */ - private _queryToString(query: string[]): string { - if (query.length !== 2) { - throw new Error('Not a valid geohash query: ' + query); - } - return query[0] + ':' + query[1]; - } - - /** - * Removes the location from the local state and fires any events if necessary. - * - * @param key The key to be removed. - * @param currentLocation The current location as [latitude, longitude] pair or null if removed. - */ - private _removeLocation(key: string, currentLocation?: number[]): void { - const locationDict = this._locationsTracked[key]; - delete this._locationsTracked[key]; - if (typeof locationDict !== 'undefined' && locationDict.isInQuery) { - const distanceFromCenter: number = (currentLocation) ? GeoFirestore.distance(currentLocation, this._center) : null; - this._fireCallbacksForKey('key_exited', key, currentLocation, distanceFromCenter); - } - } - - /** - * Decodes a query string to a query - * - * @param str The encoded query. - * @returns The decoded query as a [start, end] pair. - */ - private _stringToQuery(str: string): string[] { - const decoded: string[] = str.split(':'); - if (decoded.length !== 2) { - throw new Error('Invalid internal state! Not a valid geohash query: ' + str); - } - return decoded; - } - - /** - * Callback for any updates to locations. Will update the information about a key and fire any necessary - * events every time the key's location changes. - * - * When a key is removed from GeoFirestore or the query, this function will be called with null and performs - * any necessary cleanup. - * - * @param key The key of the GeoFirestore location. - * @param location The location as [latitude, longitude] pair. - */ - private _updateLocation(key: string, location?: number[]): void { - validateLocation(location); - // Get the key and location - let distanceFromCenter: number, isInQuery; - const wasInQuery: boolean = (key in this._locationsTracked) ? this._locationsTracked[key].isInQuery : false; - const oldLocation: number[] = (key in this._locationsTracked) ? this._locationsTracked[key].location : null; - - // Determine if the location is within this query - distanceFromCenter = GeoFirestore.distance(location, this._center); - isInQuery = (distanceFromCenter <= this._radius); - - // Add this location to the locations queried dictionary even if it is not within this query - this._locationsTracked[key] = { - location: location, - distanceFromCenter: distanceFromCenter, - isInQuery: isInQuery, - geohash: encodeGeohash(location) - }; - - // Fire the 'key_entered' event if the provided key has entered this query - if (isInQuery && !wasInQuery) { - this._fireCallbacksForKey('key_entered', key, location, distanceFromCenter); - } else if (isInQuery && oldLocation !== null && (location[0] !== oldLocation[0] || location[1] !== oldLocation[1])) { - this._fireCallbacksForKey('key_moved', key, location, distanceFromCenter); - } else if (!isInQuery && wasInQuery) { - this._fireCallbacksForKey('key_exited', key, location, distanceFromCenter); - } - } -} \ No newline at end of file diff --git a/src/tools/callbackRegistration.ts b/src/geoFire/geoCallbackRegistration.ts similarity index 100% rename from src/tools/callbackRegistration.ts rename to src/geoFire/geoCallbackRegistration.ts diff --git a/src/tools/utils.ts b/src/geoFire/geoFireUtils.ts similarity index 99% rename from src/tools/utils.ts rename to src/geoFire/geoFireUtils.ts index df06f710..55cd1c13 100644 --- a/src/tools/utils.ts +++ b/src/geoFire/geoFireUtils.ts @@ -1,6 +1,6 @@ import * as firebase from 'firebase'; -import { GeoFireObj } from '../interfaces'; +import { GeoFireObj } from './interfaces'; // Default geohash length export const g_GEOHASH_PRECISION: number = 10; diff --git a/src/firebase/query.ts b/src/geoFire/geoQuery.ts similarity index 98% rename from src/firebase/query.ts rename to src/geoFire/geoQuery.ts index d08666b5..e65fec77 100644 --- a/src/firebase/query.ts +++ b/src/geoFire/geoQuery.ts @@ -1,15 +1,15 @@ import * as firebase from 'firebase'; -import { GeoFire } from './'; -import { GeoCallbackRegistration } from '../tools/callbackRegistration'; -import { decodeGeoFireObject, encodeGeohash, geoFireGetKey, geohashQueries, validateCriteria, validateLocation } from '../tools/utils'; +import { GeoFire } from './index'; +import { GeoCallbackRegistration } from './geoCallbackRegistration'; +import { decodeGeoFireObject, encodeGeohash, geoFireGetKey, geohashQueries, validateCriteria, validateLocation } from './geoFireUtils'; -import { QueryCriteria } from '../interfaces'; +import { QueryCriteria } from './interfaces'; /** - * Creates a GeoFireQuery instance. + * Creates a GeoQuery instance. */ -export class GeoFireQuery { +export class GeoQuery { // Event callbacks private _callbacks: any = { ready: [], key_entered: [], key_exited: [], key_moved: [] }; // Variable to track when the query is cancelled diff --git a/src/firebase/index.ts b/src/geoFire/index.ts similarity index 91% rename from src/firebase/index.ts rename to src/geoFire/index.ts index f1794e7e..00fa3d57 100644 --- a/src/firebase/index.ts +++ b/src/geoFire/index.ts @@ -12,10 +12,10 @@ import * as firebase from 'firebase'; -import { GeoFireQuery } from './query'; -import { decodeGeoFireObject, degreesToRadians, encodeGeoFireObject, encodeGeohash, validateLocation, validateKey } from '../tools/utils'; +import { GeoQuery } from './geoQuery'; +import { decodeGeoFireObject, degreesToRadians, encodeGeoFireObject, encodeGeohash, validateLocation, validateKey } from './geoFireUtils'; -import { QueryCriteria } from '../interfaces'; +import { QueryCriteria } from './interfaces'; /** * Creates a GeoFire instance. @@ -120,13 +120,13 @@ export class GeoFire { }; /** - * Returns a new GeoFireQuery instance with the provided queryCriteria. + * Returns a new GeoQuery instance with the provided queryCriteria. * - * @param queryCriteria The criteria which specifies the GeoFireQuery's center and radius. - * @return A new GeoFireQuery object. + * @param queryCriteria The criteria which specifies the GeoQuery's center and radius. + * @return A new GeoQuery object. */ - public query(queryCriteria: QueryCriteria): GeoFireQuery { - return new GeoFireQuery(this._firebaseRef, queryCriteria); + public query(queryCriteria: QueryCriteria): GeoQuery { + return new GeoQuery(this._firebaseRef, queryCriteria); }; /********************/ diff --git a/src/interfaces/geoFireObj.ts b/src/geoFire/interfaces/geoFireObj.ts similarity index 100% rename from src/interfaces/geoFireObj.ts rename to src/geoFire/interfaces/geoFireObj.ts diff --git a/src/interfaces/index.ts b/src/geoFire/interfaces/index.ts similarity index 59% rename from src/interfaces/index.ts rename to src/geoFire/interfaces/index.ts index 709e52ea..1c3818cc 100644 --- a/src/interfaces/index.ts +++ b/src/geoFire/interfaces/index.ts @@ -1,3 +1,2 @@ export * from './geoFireObj'; -export * from './geoFirestoreQueryState'; export * from './queryCriteria'; \ No newline at end of file diff --git a/src/interfaces/queryCriteria.ts b/src/geoFire/interfaces/queryCriteria.ts similarity index 100% rename from src/interfaces/queryCriteria.ts rename to src/geoFire/interfaces/queryCriteria.ts diff --git a/src/index.ts b/src/index.ts index 7c1c994c..e09fdafd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,2 @@ -export * from './interfaces'; -export { GeoFire } from './firebase'; -export { GeoFirestore } from './firestore'; \ No newline at end of file +export { GeoFire } from './geoFire'; +export * from './geoFire/interfaces'; \ No newline at end of file diff --git a/src/interfaces/geoFirestoreQueryState.ts b/src/interfaces/geoFirestoreQueryState.ts deleted file mode 100644 index 8fa27cae..00000000 --- a/src/interfaces/geoFirestoreQueryState.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface GeoFirestoreQueryState { - active: boolean; - childCallback: Function; - valueCallback: Function; -} \ No newline at end of file diff --git a/test/common.ts b/test/common.ts index 67635c1b..8b2dec1a 100644 --- a/test/common.ts +++ b/test/common.ts @@ -1,10 +1,9 @@ import * as chai from 'chai'; import * as firebase from 'firebase'; -import 'firebase/firestore'; -import { GeoFire, GeoFirestore } from '../src'; -import { GeoFireQuery } from '../src/firebase/query'; -import { GeoFirestoreQuery } from '../src/firestore/query'; +import { GeoFire } from '../src/geoFire'; +import { GeoQuery } from '../src/geoFire/geoQuery'; + /*************/ /* GLOBALS */ @@ -24,19 +23,13 @@ export const invalidQueryCriterias = [{}, { random: 100 }, { center: [91, 2], ra // Create global constiables to hold the Firebase and GeoFire constiables export let geoFireRef: firebase.database.Reference, geoFire: GeoFire, - geoFireQueries: GeoFireQuery[] = []; -export let geoFirestoreRef: firebase.firestore.CollectionReference, - geoFirestore: GeoFirestore, - geoFirestoreQueries: GeoFirestoreQuery[] = []; + geoQueries: GeoQuery[] = []; // Initialize Firebase const config = { - apiKey: 'AIzaSyDfEvcG1WHlI8o_KKK2yQcTHkz63_8nZK8', - databaseURL: 'https://geofi-5653f.firebaseio.com', - projectId: 'geofi-5653f' - // apiKey: 'AIzaSyC5IcRccDo289TTRa3Y7qJIu8YPz3EnKAI', - // databaseURL: 'https://geofire-9d0de.firebaseio.com', - // projectId: 'geofire-9d0de' + apiKey: 'AIzaSyC5IcRccDo289TTRa3Y7qJIu8YPz3EnKAI', + databaseURL: 'https://geofire-9d0de.firebaseio.com', + projectId: 'geofire-9d0de' }; firebase.initializeApp(config); @@ -51,28 +44,16 @@ export function beforeEachHelper(done) { geoFire = new GeoFire(geoFireRef); // Reset the GeoFireQueries - geoFireQueries = []; - - done(); -} - -export function beforeEachHelperFirestore(done) { - // Create a new Firebase database ref at a random node - geoFirestoreRef = firebase.firestore().collection('geofire'); - // Create a new GeoFire instance - geoFirestore = new GeoFirestore(geoFirestoreRef); - - // Reset the GeoFirestoreQueries - geoFirestoreQueries = []; + geoQueries = []; done(); } /* Helper functions which runs after each Jasmine test has completed */ export function afterEachHelper(done) { - // Cancel each outstanding GeoFireQuery - geoFireQueries.forEach((geoFireQuery) => { - geoFireQuery.cancel(); + // Cancel each outstanding GeoQuery + geoQueries.forEach((GeoQuery) => { + GeoQuery.cancel(); }); geoFireRef.remove().then(() => { @@ -81,18 +62,6 @@ export function afterEachHelper(done) { }).then(done); } -export function afterEachHelperFirestore(done) { - // Cancel each outstanding GeoFirestoreQuery - geoFirestoreQueries.forEach((geoFirestoreQuery) => { - geoFirestoreQuery.cancel(); - }); - - deleteCollection(geoFirestoreRef.firestore, 'geofire', 50).then(() => { - // Wait for 50 milliseconds after each test to give enough time for old query events to expire - return wait(50); - }).then(done); -} - /* Returns a random alphabetic string of constiable length */ export function generateRandomString() { const possibleCharacters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; @@ -113,18 +82,6 @@ export function getFirebaseData() { }); }; -/* Returns the current data in the Firestore */ -export function getFirestoreData() { - return geoFirestoreRef.get().then((querySnapshot: firebase.firestore.QuerySnapshot) => { - const data = {}; - querySnapshot.docs.map((val: firebase.firestore.QueryDocumentSnapshot) => { - data[val.id] = val.data(); - }); - return data; - }); -}; - - /* Returns a promise which is fulfilled after the inputted number of milliseconds pass */ export function wait(milliseconds) { return new Promise(function (resolve) { @@ -172,31 +129,3 @@ export function Checklist(items, expect, done) { export function failTestOnCaughtError(error) { expect(error).to.throw(); } - -/* Used to purge Firestore collection. Used by afterEachHelperFirestore. */ -function deleteCollection(db: firebase.firestore.Firestore, collectionPath: string, batchSize: number) { - const collectionRef = db.collection(collectionPath); - const query: firebase.firestore.Query = collectionRef.limit(batchSize); - - return new Promise((resolve, reject) => deleteQueryBatch(db, query, batchSize, resolve, reject)); -} - -/* Actually purges Firestore collection recursively through batch function. */ -function deleteQueryBatch(db: firebase.firestore.Firestore, query: firebase.firestore.Query, batchSize: number, resolve: Function, reject: Function) { - query.get().then((snapshot) => { - // When there are no documents left, we are done - if (snapshot.size == 0) { return 0; } - - // Delete documents in a batch - const batch = db.batch(); - snapshot.docs.forEach((doc) => batch.delete(doc.ref)); - - return batch.commit().then(() => snapshot.size); - }).then((numDeleted) => { - if (numDeleted === 0) { - resolve(); - return; - } - process.nextTick(() => deleteQueryBatch(db, query, batchSize, resolve, reject)); - }).catch(err => reject(err)); -} \ No newline at end of file diff --git a/test/firestore/index.test.ts b/test/firestore/index.test.ts deleted file mode 100755 index f61e1df2..00000000 --- a/test/firestore/index.test.ts +++ /dev/null @@ -1,848 +0,0 @@ -import * as chai from 'chai'; - -import { GeoFirestore } from '../../src/firestore'; -import { GeoFirestoreQuery } from '../../src/firestore/query'; -import { - afterEachHelperFirestore, beforeEachHelperFirestore, Checklist, failTestOnCaughtError, geoFirestore, geoFirestoreRef, getFirestoreData, geoFirestoreQueries, - invalidFirebaseRefs, invalidKeys, invalidLocations, invalidQueryCriterias, validKeys, validLocations, validQueryCriterias -} from '../common'; - -const expect = chai.expect; - -describe('GeoFirestore Tests:', () => { - // Reset the Firestore before each test - beforeEach((done) => { - beforeEachHelperFirestore(done); - }); - - afterEach((done) => { - afterEachHelperFirestore(done); - }); - - describe('Constructor:', () => { - it('Constructor throws errors given invalid Firestore Collection references', () => { - invalidFirebaseRefs.forEach((invalidFirebaseRef) => { - // @ts-ignore - expect(() => new GeoFirestore(invalidFirebaseRef)).to.throw(null, 'collectionRef must be an instance of a Firestore Collection'); - }); - }); - - it('Constructor does not throw errors given valid Firestore Collection references', () => { - expect(() => new GeoFirestore(geoFirestoreRef)).not.to.throw(); - }); - }); - - describe('ref():', () => { - it('ref() returns the Firestore Collection reference used to create a GeoFire instance', () => { - expect(geoFirestore.ref()).to.deep.equal(geoFirestoreRef); - }); - }); - - describe('Adding a single location via set():', () => { - it('set() returns a promise', (done) => { - - const cl = new Checklist(['p1'], expect, done); - - geoFirestore.set('loc1', [0, 0]).then(() => { - cl.x('p1'); - }); - }); - - it('set() updates Firebase when adding new locations', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); - - geoFirestore.set('loc1', [0, 0]).then(() => { - cl.x('p1'); - - return geoFirestore.set('loc2', [50, 50]); - }).then(() => { - cl.x('p2'); - - return geoFirestore.set('loc3', [-90, -90]); - }).then(() => { - cl.x('p3'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' }, - 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [50, 50], 'g': 'v0gs3y0zh7' }, - 'loc3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' } - }); - - cl.x('p4'); - }).catch((error) => { - failTestOnCaughtError(error) - }); - }); - - it('set() handles decimal latitudes and longitudes', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); - - geoFirestore.set('loc1', [0.254, 0]).then(() => { - cl.x('p1'); - - return geoFirestore.set('loc2', [50, 50.293403]); - }).then(() => { - cl.x('p2'); - - return geoFirestore.set('loc3', [-82.614, -90.938]); - }).then(() => { - cl.x('p3'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': 'ebpcrypzxv', 'l': [0.254, 0], 'g': 'ebpcrypzxv' }, - 'loc2': { '.priority': 'v0gu2qnx15', 'l': [50, 50.293403], 'g': 'v0gu2qnx15' }, - 'loc3': { '.priority': '1cr648sfx4', 'l': [-82.614, -90.938], 'g': '1cr648sfx4' } - }); - - cl.x('p4'); - }).catch(failTestOnCaughtError); - }); - - it('set() updates Firebase when changing a pre-existing key', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); - - geoFirestore.set('loc1', [0, 0]).then(() => { - cl.x('p1'); - - return geoFirestore.set('loc2', [50, 50]); - }).then(() => { - cl.x('p2'); - - return geoFirestore.set('loc3', [-90, -90]); - }).then(() => { - cl.x('p3'); - - return geoFirestore.set('loc1', [2, 3]); - }).then(() => { - cl.x('p4'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': 's065kk0dc5', 'l': [2, 3], 'g': 's065kk0dc5' }, - 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [50, 50], 'g': 'v0gs3y0zh7' }, - 'loc3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' } - }); - - cl.x('p5'); - }).catch(failTestOnCaughtError); - }); - - it('set() updates Firebase when changing a pre-existing key to the same location', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); - - geoFirestore.set('loc1', [0, 0]).then(() => { - cl.x('p1'); - - return geoFirestore.set('loc2', [50, 50]); - }).then(() => { - cl.x('p2'); - - return geoFirestore.set('loc3', [-90, -90]); - }).then(() => { - cl.x('p3'); - - return geoFirestore.set('loc1', [0, 0]); - }).then(() => { - cl.x('p4'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' }, - 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [50, 50], 'g': 'v0gs3y0zh7' }, - 'loc3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' } - }); - - cl.x('p5'); - }).catch(failTestOnCaughtError); - }); - - it('set() handles multiple keys at the same location', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); - - geoFirestore.set('loc1', [0, 0]).then(() => { - cl.x('p1'); - - return geoFirestore.set('loc2', [0, 0]); - }).then(() => { - cl.x('p2'); - - return geoFirestore.set('loc3', [0, 0]); - }).then(() => { - cl.x('p3'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' }, - 'loc2': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' }, - 'loc3': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' } - }); - - cl.x('p4'); - }).catch(failTestOnCaughtError); - }); - - it('set() updates Firebase after complex operations', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9', 'p10', 'p11'], expect, done); - - geoFirestore.set('loc:1', [0, 0]).then(() => { - cl.x('p1'); - - return geoFirestore.set('loc2', [50, 50]); - }).then(() => { - cl.x('p2'); - - return geoFirestore.set('loc%!A72f()3', [-90, -90]); - }).then(() => { - cl.x('p3'); - - return geoFirestore.remove('loc2'); - }).then(() => { - cl.x('p4'); - - return geoFirestore.set('loc2', [0.2358, -72.621]); - }).then(() => { - cl.x('p5'); - - return geoFirestore.set('loc4', [87.6, -130]); - }).then(() => { - cl.x('p6'); - - return geoFirestore.set('loc5', [5, 55.555]); - }).then(() => { - cl.x('p7'); - - return geoFirestore.set('loc5', null); - }).then(() => { - cl.x('p8'); - - return geoFirestore.set('loc:1', [87.6, -130]); - }).then(() => { - cl.x('p9'); - - return geoFirestore.set('loc6', [-72.258, 0.953215]); - }).then(() => { - cl.x('p10'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc:1': { '.priority': 'cped3g0fur', 'l': [87.6, -130], 'g': 'cped3g0fur' }, - 'loc2': { '.priority': 'd2h376zj8h', 'l': [0.2358, -72.621], 'g': 'd2h376zj8h' }, - 'loc%!A72f()3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' }, - 'loc4': { '.priority': 'cped3g0fur', 'l': [87.6, -130], 'g': 'cped3g0fur' }, - 'loc6': { '.priority': 'h50svty4es', 'l': [-72.258, 0.953215], 'g': 'h50svty4es' } - }); - - cl.x('p11'); - }).catch(failTestOnCaughtError); - }); - - it('set() does not throw errors given valid keys', () => { - validKeys.forEach((validKey) => { - expect(() => { - geoFirestore.set(validKey, [0, 0]); - }).not.to.throw(); - }); - }); - - it('set() throws errors given invalid keys', () => { - invalidKeys.forEach((invalidKey) => { - expect(() => { - geoFirestore.set(invalidKey, [0, 0]); - }).to.throw(); - }); - }); - - it('set() does not throw errors given valid locations', () => { - validLocations.forEach((validLocation) => { - expect(() => { - geoFirestore.set('loc', validLocation); - }).not.to.throw(); - }); - }); - - it('set() throws errors given invalid locations', () => { - invalidLocations.forEach((invalidLocation) => { - // Setting location to null is valid since it will remove the key - if (invalidLocation !== null) { - expect(() => { - // @ts-ignore - geoFirestore.set('loc', invalidLocation); - }).to.throw(Error, /Invalid GeoFire location/); - } - }); - }); - }); - - describe('Adding multiple locations via set():', () => { - it('set() returns a promise', (done) => { - - const cl = new Checklist(['p1'], expect, done); - - geoFirestore.set({ - 'loc1': [0, 0] - }).then(() => { - cl.x('p1'); - }); - }); - - it('set() updates Firebase when adding new locations', (done) => { - const cl = new Checklist(['p1', 'p2'], expect, done); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, 50], - 'loc3': [-90, -90] - }).then(() => { - cl.x('p1'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' }, - 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [50, 50], 'g': 'v0gs3y0zh7' }, - 'loc3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' } - }); - - cl.x('p2'); - }).catch(failTestOnCaughtError); - }); - - it('set() handles decimal latitudes and longitudes', (done) => { - const cl = new Checklist(['p1', 'p2'], expect, done); - - geoFirestore.set({ - 'loc1': [0.254, 0], - 'loc2': [50, 50.293403], - 'loc3': [-82.614, -90.938] - }).then(() => { - cl.x('p1'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': 'ebpcrypzxv', 'l': [0.254, 0], 'g': 'ebpcrypzxv' }, - 'loc2': { '.priority': 'v0gu2qnx15', 'l': [50, 50.293403], 'g': 'v0gu2qnx15' }, - 'loc3': { '.priority': '1cr648sfx4', 'l': [-82.614, -90.938], 'g': '1cr648sfx4' } - }); - - cl.x('p2'); - }).catch(failTestOnCaughtError); - }); - - it('set() updates Firebase when changing a pre-existing key', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, 50], - 'loc3': [-90, -90] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [2, 3] - }); - }).then(() => { - cl.x('p2'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': 's065kk0dc5', 'l': [2, 3], 'g': 's065kk0dc5' }, - 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [50, 50], 'g': 'v0gs3y0zh7' }, - 'loc3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' } - }); - - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('set() updates Firebase when changing a pre-existing key to the same location', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, 50], - 'loc3': [-90, -90] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [0, 0] - }); - }).then(() => { - cl.x('p2'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' }, - 'loc2': { '.priority': 'v0gs3y0zh7', 'l': [50, 50], 'g': 'v0gs3y0zh7' }, - 'loc3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' } - }); - - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('set() handles multiple keys at the same location', (done) => { - const cl = new Checklist(['p1', 'p2'], expect, done); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [0, 0], - 'loc3': [0, 0] - }).then(() => { - cl.x('p1'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' }, - 'loc2': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' }, - 'loc3': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' } - }); - - cl.x('p2'); - }).catch(failTestOnCaughtError); - }); - - it('set() updates Firebase after complex operations', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6'], expect, done); - - geoFirestore.set({ - 'loc:1': [0, 0], - 'loc2': [50, 50], - 'loc%!A72f()3': [-90, -90] - }).then(() => { - cl.x('p1'); - - return geoFirestore.remove('loc2'); - }).then(() => { - cl.x('p2'); - - return geoFirestore.set({ - 'loc2': [0.2358, -72.621], - 'loc4': [87.6, -130], - 'loc5': [5, 55.555] - }); - }).then(() => { - cl.x('p3'); - - return geoFirestore.set({ - 'loc5': null - }); - }).then(() => { - cl.x('p4'); - - return geoFirestore.set({ - 'loc:1': [87.6, -130], - 'loc6': [-72.258, 0.953215] - }); - }).then(() => { - cl.x('p5'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc:1': { '.priority': 'cped3g0fur', 'l': [87.6, -130], 'g': 'cped3g0fur' }, - 'loc2': { '.priority': 'd2h376zj8h', 'l': [0.2358, -72.621], 'g': 'd2h376zj8h' }, - 'loc%!A72f()3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' }, - 'loc4': { '.priority': 'cped3g0fur', 'l': [87.6, -130], 'g': 'cped3g0fur' }, - 'loc6': { '.priority': 'h50svty4es', 'l': [-72.258, 0.953215], 'g': 'h50svty4es' } - }); - - cl.x('p6'); - }).catch(failTestOnCaughtError); - }); - - it('set() does not throw errors given valid keys', () => { - validKeys.forEach((validKey) => { - expect(() => { - const locations = {}; - locations[validKey] = [0, 0]; - geoFirestore.set(locations); - }).not.to.throw(); - }); - }); - - it('set() throws errors given invalid keys', () => { - invalidKeys.forEach((invalidKey) => { - if (invalidKey !== null && invalidKey !== undefined && typeof invalidKey !== 'boolean') { - expect(() => { - const locations = {}; - // @ts-ignore - locations[invalidKey] = [0, 0]; - geoFirestore.set(locations); - }).to.throw(); - } - }); - }); - - it('set() throws errors given a location argument in combination with an object', () => { - expect(() => { - geoFirestore.set({ - 'loc': [0, 0] - }, [0, 0]); - }).to.throw(); - }); - - it('set() does not throw errors given valid locations', () => { - validLocations.forEach((validLocation) => { - expect(() => { - geoFirestore.set({ - 'loc': validLocation - }); - }).not.to.throw(); - }); - }); - - it('set() throws errors given invalid locations', () => { - invalidLocations.forEach((invalidLocation) => { - // Setting location to null is valid since it will remove the key - if (invalidLocation !== null) { - expect(() => { - geoFirestore.set({ - 'loc': invalidLocation - }); - }).to.throw(Error, /Invalid GeoFire location/); - } - }); - }); - }); - - describe('Retrieving locations:', () => { - it('get() returns a promise', (done) => { - const cl = new Checklist(['p1'], expect, done); - - geoFirestore.get('loc1').then((ref) => { - cl.x('p1'); - }); - }); - - it('get() returns null for non-existent keys', (done) => { - const cl = new Checklist(['p1'], expect, done); - - geoFirestore.get('loc1').then((location) => { - expect(location).to.equal(null); - - cl.x('p1'); - }); - }); - - it('get() retrieves locations given existing keys', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, 50], - 'loc3': [-90, -90] - }).then(() => { - cl.x('p1'); - - return geoFirestore.get('loc1'); - }).then((location) => { - expect(location).to.deep.equal([0, 0]); - cl.x('p2'); - - return geoFirestore.get('loc2'); - }).then((location) => { - expect(location).to.deep.equal([50, 50]); - cl.x('p3'); - - return geoFirestore.get('loc3'); - }).then((location) => { - expect(location).to.deep.equal([-90, -90]); - cl.x('p4'); - }).catch(failTestOnCaughtError); - }); - - it('get() does not throw errors given valid keys', () => { - validKeys.forEach((validKey) => { - expect(() => geoFirestore.get(validKey)).not.to.throw(); - }); - }); - - it('get() throws errors given invalid keys', () => { - invalidKeys.forEach((invalidKey) => { - // @ts-ignore - expect(() => geoFirestore.get(invalidKey)).to.throw(); - }); - }); - }); - - describe('Removing locations:', () => { - it('set() removes existing location given null', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [2, 3] - }).then(() => { - cl.x('p1'); - - return geoFirestore.get('loc1'); - }).then((location) => { - expect(location).to.deep.equal([0, 0]); - - cl.x('p2'); - - return geoFirestore.set('loc1', null); - }).then(() => { - cl.x('p3'); - - return geoFirestore.get('loc1'); - }).then((location) => { - expect(location).to.equal(null); - - cl.x('p4'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc2': { '.priority': 's065kk0dc5', 'l': [2, 3], 'g': 's065kk0dc5' } - }); - - cl.x('p5'); - }).catch(failTestOnCaughtError); - }); - - it('set() does nothing given a non-existent location and null', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); - - geoFirestore.set('loc1', [0, 0]).then(() => { - cl.x('p1'); - - return geoFirestore.get('loc1'); - }).then((location) => { - expect(location).to.deep.equal([0, 0]); - - cl.x('p2'); - - return geoFirestore.set('loc2', null); - }).then(() => { - cl.x('p3'); - - return geoFirestore.get('loc2'); - }).then((location) => { - expect(location).to.equal(null); - - cl.x('p4'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' } - }); - - cl.x('p5'); - }).catch(failTestOnCaughtError); - }); - - it('set() removes existing location given null', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [2, 3] - }).then(() => { - cl.x('p1'); - - return geoFirestore.get('loc1'); - }).then((location) => { - expect(location).to.deep.equal([0, 0]); - - cl.x('p2'); - - return geoFirestore.set({ - 'loc1': null, - 'loc3': [-90, -90] - }); - }).then(() => { - cl.x('p3'); - - return geoFirestore.get('loc1'); - }).then((location) => { - expect(location).to.equal(null); - - cl.x('p4'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc2': { '.priority': 's065kk0dc5', 'l': [2, 3], 'g': 's065kk0dc5' }, - 'loc3': { '.priority': '1bpbpbpbpb', 'l': [-90, -90], 'g': '1bpbpbpbpb' } - }); - - cl.x('p5'); - }).catch(failTestOnCaughtError); - }); - - it('set() does nothing given a non-existent location and null', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4'], expect, done); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': null - }).then(() => { - cl.x('p1'); - - return geoFirestore.get('loc1'); - }).then((location) => { - expect(location).to.deep.equal([0, 0]); - - cl.x('p2'); - - return geoFirestore.get('loc2'); - }).then((location) => { - expect(location).to.equal(null); - - cl.x('p3'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' } - }); - - cl.x('p4'); - }).catch(failTestOnCaughtError); - }); - - it('remove() removes existing location', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); - - geoFirestore.set({ - 'loc:^%*1': [0, 0], - 'loc2': [2, 3] - }).then(() => { - cl.x('p1'); - - return geoFirestore.get('loc:^%*1'); - }).then((location) => { - expect(location).to.deep.equal([0, 0]); - - cl.x('p2'); - - return geoFirestore.remove('loc:^%*1'); - }).then(() => { - cl.x('p3'); - - return geoFirestore.get('loc:^%*1'); - }).then((location) => { - expect(location).to.equal(null); - - cl.x('p4'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc2': { '.priority': 's065kk0dc5', 'l': [2, 3], 'g': 's065kk0dc5' } - }); - - cl.x('p5'); - }).catch(failTestOnCaughtError); - }); - - it('remove() does nothing given a non-existent location', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5'], expect, done); - - geoFirestore.set('loc1', [0, 0]).then(() => { - cl.x('p1'); - - return geoFirestore.get('loc1'); - }).then((location) => { - expect(location).to.deep.equal([0, 0]); - - cl.x('p2'); - - return geoFirestore.remove('loc2'); - }).then(() => { - cl.x('p3'); - - return geoFirestore.get('loc2'); - }).then((location) => { - expect(location).to.equal(null); - - cl.x('p4'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc1': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' } - }); - - cl.x('p5'); - }).catch(failTestOnCaughtError); - }); - - it('remove() only removes one key if multiple keys are at the same location', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [2, 3], - 'loc3': [0, 0] - }).then(() => { - cl.x('p1'); - - return geoFirestore.remove('loc1'); - }).then(() => { - cl.x('p2'); - - return getFirestoreData(); - }).then((firebaseData) => { - expect(firebaseData).to.deep.equal({ - 'loc2': { '.priority': 's065kk0dc5', 'l': [2, 3], 'g': 's065kk0dc5' }, - 'loc3': { '.priority': '7zzzzzzzzz', 'l': [0, 0], 'g': '7zzzzzzzzz' } - }); - - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('remove() does not throw errors given valid keys', () => { - validKeys.forEach((validKey) => { - expect(() => geoFirestore.remove(validKey)).not.to.throw(); - }); - }); - - it('remove() throws errors given invalid keys', () => { - invalidKeys.forEach((invalidKey) => { - // @ts-ignore - expect(() => geoFirestore.remove(invalidKey)).to.throw(); - }); - }); - }); - - describe('query():', () => { - it('query() returns GeoFireQuery instance', () => { - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - expect(geoFirestoreQueries[0] instanceof GeoFirestoreQuery).to.be.ok; - }); - - it('query() does not throw errors given valid query criteria', () => { - validQueryCriterias.forEach((validQueryCriteria) => { - if (typeof validQueryCriteria.center !== 'undefined' && typeof validQueryCriteria.radius !== 'undefined') { - expect(() => geoFirestore.query(validQueryCriteria)).not.to.throw(); - } - }); - }); - - it('query() throws errors given invalid query criteria', () => { - invalidQueryCriterias.forEach((invalidQueryCriteria) => { - // @ts-ignore - expect(() => geoFirestore.query(invalidQueryCriteria)).to.throw(); - }); - }); - }); -}); diff --git a/test/firestore/query.test.ts b/test/firestore/query.test.ts deleted file mode 100644 index 9a1ac814..00000000 --- a/test/firestore/query.test.ts +++ /dev/null @@ -1,1420 +0,0 @@ -import * as chai from 'chai'; - -import { - afterEachHelperFirestore, beforeEachHelperFirestore, Checklist, failTestOnCaughtError, geoFirestore, geoFirestoreQueries, invalidQueryCriterias, validQueryCriterias, wait -} from '../common'; - -const expect = chai.expect; - -describe('GeoFirestoreQuery Tests:', () => { - // Reset the Firestore before each test - beforeEach((done) => { - beforeEachHelperFirestore(done); - }); - - afterEach((done) => { - afterEachHelperFirestore(done); - }); - - describe('Constructor:', () => { - it('Constructor stores query criteria', () => { - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - expect(geoFirestoreQueries[0].center()).to.deep.equal([1, 2]); - expect(geoFirestoreQueries[0].radius()).to.equal(1000); - }); - - it('Constructor throws error on invalid query criteria', () => { - expect(() => geoFirestore.query({})).to.throw(); - // @ts-ignore - expect(() => geoFirestore.query({ random: 100 })).to.throw(); - expect(() => geoFirestore.query({ center: [1, 2] })).to.throw(); - expect(() => geoFirestore.query({ radius: 1000 })).to.throw(); - expect(() => geoFirestore.query({ center: [91, 2], radius: 1000 })).to.throw(); - expect(() => geoFirestore.query({ center: [1, -181], radius: 1000 })).to.throw(); - // @ts-ignore - expect(() => geoFirestore.query({ center: ['text', 2], radius: 1000 })).to.throw(); - // @ts-ignore - expect(() => geoFirestore.query({ center: [1, [1, 2]], radius: 1000 })).to.throw(); - // @ts-ignore - expect(() => geoFirestore.query({ center: 1000, radius: 1000 })).to.throw(); - expect(() => geoFirestore.query({ center: null, radius: 1000 })).to.throw(); - expect(() => geoFirestore.query({ center: undefined, radius: 1000 })).to.throw(); - expect(() => geoFirestore.query({ center: [null, 2], radius: 1000 })).to.throw(); - expect(() => geoFirestore.query({ center: [1, undefined], radius: 1000 })).to.throw(); - expect(() => geoFirestore.query({ center: [1, 2], radius: -10 })).to.throw(); - // @ts-ignore - expect(() => geoFirestore.query({ center: [1, 2], radius: 'text' })).to.throw(); - // @ts-ignore - expect(() => geoFirestore.query({ center: [1, 2], radius: [1, 2] })).to.throw(); - expect(() => geoFirestore.query({ center: [1, 2], radius: null })).to.throw(); - expect(() => geoFirestore.query({ center: [1, 2], radius: undefined })).to.throw(); - // @ts-ignore - expect(() => geoFirestore.query({ center: [1, 2], radius: 1000, other: 'throw' })).to.throw(); - }); - }); - - describe('updateCriteria():', () => { - it('updateCriteria() updates query criteria', () => { - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - expect(geoFirestoreQueries[0].center()).to.deep.equal([1, 2]); - expect(geoFirestoreQueries[0].radius()).to.equal(1000); - - geoFirestoreQueries[0].updateCriteria({ center: [2, 3], radius: 100 }); - - expect(geoFirestoreQueries[0].center()).to.deep.equal([2, 3]); - expect(geoFirestoreQueries[0].radius()).to.equal(100); - }); - - it('updateCriteria() updates query criteria when given only center', () => { - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - expect(geoFirestoreQueries[0].center()).to.deep.equal([1, 2]); - expect(geoFirestoreQueries[0].radius()).to.equal(1000); - - geoFirestoreQueries[0].updateCriteria({ center: [2, 3] }); - - expect(geoFirestoreQueries[0].center()).to.deep.equal([2, 3]); - expect(geoFirestoreQueries[0].radius()).to.equal(1000); - }); - - it('updateCriteria() updates query criteria when given only radius', () => { - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - expect(geoFirestoreQueries[0].center()).to.deep.equal([1, 2]); - expect(geoFirestoreQueries[0].radius()).to.equal(1000); - - geoFirestoreQueries[0].updateCriteria({ radius: 100 }); - - expect(geoFirestoreQueries[0].center()).to.deep.equal([1, 2]); - expect(geoFirestoreQueries[0].radius()).to.equal(100); - }); - - it('updateCriteria() fires \'key_entered\' callback for locations which now belong to the GeoFirestoreQuery', (done) => { - const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [90, 90], radius: 1000 })); - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [50, -7], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [67, 55] - }).then(() => { - cl.x('p1'); - - geoFirestoreQueries[0].updateCriteria({ center: [1, 2], radius: 1000 }); - - return wait(100); - }).then(() => { - cl.x('p2'); - }).catch(failTestOnCaughtError); - }); - - it('updateCriteria() fires \'key_entered\' callback for locations with complex keys which now belong to the GeoFirestoreQuery', (done) => { - const cl = new Checklist(['p1', 'p2', 'loc:^:*1 entered', 'loc-+-+-4 entered'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [90, 90], radius: 1000 })); - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - - geoFirestore.set({ - 'loc:^:*1': [2, 3], - 'loc:a:a:a:a:2': [50, -7], - 'loc%!@3': [16, -150], - 'loc-+-+-4': [5, 5], - 'loc:5': [67, 55] - }).then(() => { - cl.x('p1'); - - geoFirestoreQueries[0].updateCriteria({ center: [1, 2], radius: 1000 }); - - return wait(100); - }).then(() => { - cl.x('p2'); - }).catch(failTestOnCaughtError); - }); - - it('updateCriteria() fires \'key_exited\' callback for locations which no longer belong to the GeoFirestoreQuery', (done) => { - const cl = new Checklist(['p1', 'p2', 'loc1 exited', 'loc4 exited'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited'); - }); - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [50, -7], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [67, 55] - }).then(() => { - cl.x('p1'); - - geoFirestoreQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); - - return wait(100); - }).then(() => { - cl.x('p2'); - }).catch(failTestOnCaughtError); - }); - - it('updateCriteria() does not cause event callbacks to fire on the previous criteria', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc1 exited', 'loc4 exited', 'loc4 entered', 'loc5 entered'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited'); - }); - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [50, -7], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [88, 88] - }).then(() => { - cl.x('p1'); - - geoFirestoreQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); - - return wait(100); - }).then(() => { - cl.x('p2'); - - return geoFirestore.set({ - 'loc2': [1, 1], - 'loc4': [89, 90] - }); - }).then(() => { - cl.x('p3'); - - return wait(100); - }).then(() => { - cl.x('p4'); - }).catch(failTestOnCaughtError); - }); - - it('updateCriteria() does not cause \'key_moved\' callbacks to fire for keys in both the previous and updated queries', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc4 exited', 'loc2 entered'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited'); - }); - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved'); - }); - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [50, -7], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [88, 88] - }).then(() => { - cl.x('p1'); - - geoFirestoreQueries[0].updateCriteria({ center: [1, 1], radius: 1000 }); - - return wait(100); - }).then(() => { - cl.x('p2'); - - return geoFirestore.set({ - 'loc2': [1, 1], - 'loc4': [89, 90] - }); - }).then(() => { - cl.x('p3'); - - return wait(100); - }).then(() => { - cl.x('p4'); - }).catch(failTestOnCaughtError); - }); - - it('updateCriteria() does not cause \'key_exited\' callbacks to fire twice for keys in the previous query but not in the updated query and which were moved after the query was updated', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'loc1 entered', 'loc4 entered', 'loc1 exited', 'loc4 exited', 'loc4 entered', 'loc5 entered', 'loc5 moved'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited'); - }); - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved'); - }); - - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [50, -7], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [88, 88] - }).then(() => { - cl.x('p1'); - - geoFirestoreQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); - - return wait(100); - }).then(() => { - cl.x('p2'); - - return geoFirestore.set({ - 'loc2': [1, 1], - 'loc4': [89, 90] - }); - }).then(() => { - cl.x('p3'); - - return wait(100); - }).then(() => { - cl.x('p4'); - - return geoFirestore.set({ - 'loc2': [0, 0], - 'loc5': [89, 89] - }); - }).then(() => { - cl.x('p5'); - - return wait(100); - }).then(() => { - cl.x('p6'); - }).catch(failTestOnCaughtError); - }); - - it('updateCriteria() does not throw errors given valid query criteria', () => { - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - validQueryCriterias.forEach((validQueryCriteria) => { - expect(() => geoFirestoreQueries[0].updateCriteria(validQueryCriteria)).not.to.throw(); - }); - }); - - it('updateCriteria() throws errors given invalid query criteria', () => { - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - invalidQueryCriterias.forEach((invalidQueryCriteria) => { - // @ts-ignore - expect(() => geoFirestoreQueries[0].updateCriteria(invalidQueryCriteria)).to.throw(); - }); - }); - }); - - describe('on():', () => { - it('on() throws error given invalid event type', () => { - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - const setInvalidEventType = () => { - geoFirestoreQueries[0].on('invalid_event', () => { }); - } - - expect(setInvalidEventType).to.throw(); - }); - - it('on() throws error given invalid callback', () => { - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - const setInvalidCallback = () => { - // @ts-ignore - geoFirestoreQueries[0].on('key_entered', 'non-function'); - } - - expect(setInvalidCallback).to.throw(); - }); - }); - - describe('\'ready\' event:', () => { - it('\'ready\' event fires after all \'key_entered\' events have fired', (done) => { - const cl = new Checklist(['p1', 'loc1 entered', 'loc2 entered', 'loc5 entered', 'loc6 entered', 'loc7 entered', 'loc10 entered', 'ready fired'], expect, done); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [1, 1], - 'loc3': [50, 50], - 'loc4': [14, 1], - 'loc5': [1, 2], - 'loc6': [1, 1], - 'loc7': [0, 0], - 'loc8': [-80, 44], - 'loc9': [1, -136], - 'loc10': [-2, -2] - }).then(() => { - cl.x('p1'); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - - geoFirestoreQueries[0].on('ready', () => { - expect(cl.length()).to.be.equal(1); - cl.x('ready fired'); - }); - }); - }); - - it('\'ready\' event fires immediately if the callback is added after the query is already ready', (done) => { - const cl = new Checklist(['p1', 'loc1 entered', 'loc2 entered', 'loc5 entered', 'loc6 entered', 'loc7 entered', 'loc10 entered', 'ready1 fired', 'ready2 fired'], expect, done); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [1, 1], - 'loc3': [50, 50], - 'loc4': [14, 1], - 'loc5': [1, 2], - 'loc6': [1, 1], - 'loc7': [0, 0], - 'loc8': [-80, 44], - 'loc9': [1, -136], - 'loc10': [-2, -2] - }).then(() => { - cl.x('p1'); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - - geoFirestoreQueries[0].on('ready', () => { - expect(cl.length()).to.be.equal(2); - cl.x('ready1 fired'); - geoFirestoreQueries[0].on('ready', () => { - expect(cl.length()).to.be.equal(1); - cl.x('ready2 fired'); - }); - }); - }); - }); - - it('\'ready\' event fires after increasing the query radius, even if no new geohashes were queried', (done) => { - const cl = new Checklist(['ready1 fired', 'ready2 fired'], expect, done); - geoFirestoreQueries.push(geoFirestore.query({ center: [37.7851382, -122.405893], radius: 6 })); - const onReadyCallbackRegistration1 = geoFirestoreQueries[0].on('ready', () => { - cl.x('ready1 fired'); - onReadyCallbackRegistration1.cancel(); - geoFirestoreQueries[0].updateCriteria({ - radius: 7 - }); - geoFirestoreQueries[0].on('ready', () => { - cl.x('ready2 fired'); - }); - }); - }); - - it('updateCriteria() fires the \'ready\' event after all \'key_entered\' events have fired', (done) => { - const cl = new Checklist(['p1', 'loc1 entered', 'loc2 entered', 'loc5 entered', 'loc3 entered', 'loc1 exited', 'loc2 exited', 'loc5 exited', 'ready1 fired', 'ready2 fired'], expect, done); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [1, 1], - 'loc3': [50, 50], - 'loc4': [14, 1], - 'loc5': [1, 2] - }).then(() => { - cl.x('p1'); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited'); - }); - - const onReadyCallbackRegistration1 = geoFirestoreQueries[0].on('ready', () => { - expect(cl.length()).to.be.equal(6); - cl.x('ready1 fired'); - - onReadyCallbackRegistration1.cancel(); - - geoFirestoreQueries[0].updateCriteria({ - center: [51, 51] - }); - - geoFirestoreQueries[0].on('ready', () => { - expect(cl.length()).to.be.equal(1); - cl.x('ready2 fired'); - }); - }); - }); - }); - }); - - describe('\'key_moved\' event:', () => { - it('\'key_moved\' callback does not fire for brand new locations within or outside of the GeoFirestoreQuery', (done) => { - const cl = new Checklist(['p1', 'p2'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved'); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, -7], - 'loc3': [1, 1] - }).then(() => { - cl.x('p1'); - - return wait(100); - }).then(() => { - cl.x('p2'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_moved\' callback does not fire for locations outside of the GeoFirestoreQuery which are moved somewhere else outside of the GeoFirestoreQuery', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved'); - }); - - geoFirestore.set({ - 'loc1': [1, 90], - 'loc2': [50, -7], - 'loc3': [16, -150] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [1, 91], - 'loc3': [-50, -50] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_moved\' callback does not fire for locations outside of the GeoFirestoreQuery which are moved within the GeoFirestoreQuery', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved'); - }); - - geoFirestore.set({ - 'loc1': [1, 90], - 'loc2': [50, -7], - 'loc3': [16, -150] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [0, 0], - 'loc3': [-1, -1] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_moved\' callback does not fire for locations within the GeoFirestoreQuery which are moved somewhere outside of the GeoFirestoreQuery', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved'); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, -7], - 'loc3': [1, 1] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [1, 90], - 'loc3': [-1, -90] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_moved\' callback does not fires for a location within the GeoFirestoreQuery which is set to the same location', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc3 moved'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved'); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, -7], - 'loc3': [1, -1] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [55, 55], - 'loc3': [1, 1] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_moved\' callback fires for locations within the GeoFirestoreQuery which are moved somewhere else within the GeoFirestoreQuery', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved'); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, -7], - 'loc3': [1, 1] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [2, 2], - 'loc3': [-1, -1] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_moved\' callback gets passed correct location parameter', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved to 2,2', 'loc3 moved to -1,-1'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved to ' + location); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, -7], - 'loc3': [1, 1] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [2, 2], - 'loc3': [-1, -1] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_moved\' callback gets passed correct distance parameter', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved (111.19 km from center)', 'loc3 moved (400.90 km from center)'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved (' + distance.toFixed(2) + ' km from center)'); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, -7], - 'loc3': [1, 1] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [2, 2], - 'loc3': [-1, -1] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_moved\' callback properly fires when multiple keys are at the same location within the GeoFirestoreQuery and only one of them moves somewhere else within the GeoFirestoreQuery', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved'); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [0, 0], - 'loc3': [1, 1] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [2, 2], - 'loc3': [-1, -1] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_moved\' callback properly fires when a location within the GeoFirestoreQuery moves somehwere else within the GeoFirestoreQuery that is already occupied by another key', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved'); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [2, 2], - 'loc3': [1, 1] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [2, 2], - 'loc3': [-1, -1] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('multiple \'key_moved\' callbacks fire for locations within the GeoFirestoreQuery which are moved somewhere else within the GeoFirestoreQuery', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved1', 'loc3 moved1', 'loc1 moved2', 'loc3 moved2'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved1'); - }); - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved2'); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, -7], - 'loc3': [1, 1] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [2, 2], - 'loc3': [-1, -1] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - }); - - describe('\'key_entered\' event:', () => { - it('\'key_entered\' callback fires when a location enters the GeoFirestoreQuery before onKeyEntered() was called', (done) => { - const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [50, -7], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [67, 55] - }).then(() => { - cl.x('p1'); - - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - - return wait(100); - }).then(() => { - cl.x('p2'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_entered\' callback fires when a location enters the GeoFirestoreQuery after onKeyEntered() was called', (done) => { - const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [50, -7], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [67, 55] - }).then(() => { - cl.x('p1'); - - return wait(100); - }).then(() => { - cl.x('p2'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_entered\' callback gets passed correct location parameter', (done) => { - const cl = new Checklist(['p1', 'p2', 'loc1 entered at 2,3', 'loc4 entered at 5,5'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered at ' + location); - }); - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [50, -7], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [67, 55] - }).then(() => { - cl.x('p1'); - - return wait(100); - }).then(() => { - cl.x('p2'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_entered\' callback gets passed correct distance parameter', (done) => { - const cl = new Checklist(['p1', 'p2', 'loc1 entered (157.23 km from center)', 'loc4 entered (555.66 km from center)'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered (' + distance.toFixed(2) + ' km from center)'); - }); - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [50, -7], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [67, 55] - }).then(() => { - cl.x('p1'); - - return wait(100); - }).then(() => { - cl.x('p2'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_entered\' callback properly fires when multiple keys are at the same location outside the GeoFirestoreQuery and only one of them moves within the GeoFirestoreQuery', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 entered'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - - geoFirestore.set({ - 'loc1': [50, 50], - 'loc2': [50, 50], - 'loc3': [18, -121] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set('loc1', [2, 2]); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_entered\' callback properly fires when a location outside the GeoFirestoreQuery moves somewhere within the GeoFirestoreQuery that is already occupied by another key', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 entered', 'loc3 entered'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - - geoFirestore.set({ - 'loc1': [50, 50], - 'loc2': [50, 50], - 'loc3': [0, 0] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set('loc1', [0, 0]); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('multiple \'key_entered\' callbacks fire when a location enters the GeoFirestoreQuery', (done) => { - const cl = new Checklist(['p1', 'p2', 'loc1 entered1', 'loc4 entered1', 'loc1 entered2', 'loc4 entered2'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered1'); - }); - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered2'); - }); - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [50, -7], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [67, 55] - }).then(() => { - cl.x('p1'); - - return wait(100); - }).then(() => { - cl.x('p2'); - }).catch(failTestOnCaughtError); - }); - }); - - describe('\'key_exited\' event:', () => { - it('\'key_exited\' callback fires when a location leaves the GeoFirestoreQuery', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited', 'loc4 exited'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited'); - }); - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [50, -7], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [67, 55] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [25, 90], - 'loc4': [25, 5] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_exited\' callback gets passed correct location parameter', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited to 25,90', 'loc4 exited to 25,5'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited to ' + location); - }); - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [5, 2], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [67, 55] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [25, 90], - 'loc2': [5, 5], - 'loc4': [25, 5] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_exited\' callback gets passed correct distance parameter', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited (9759.01 km from center)', 'loc4 exited (2688.06 km from center)'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited (' + distance.toFixed(2) + ' km from center)'); - }); - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [5, 2], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [67, 55] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [25, 90], - 'loc2': [5, 5], - 'loc4': [25, 5] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_exited\' callback gets passed null for location and distance parameters if the key is entirely removed from GeoFirestore', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - expect(location).to.be.equal(null); - expect(distance).to.be.equal(null); - cl.x(key + ' exited'); - }); - - geoFirestore.set('loc1', [2, 3]).then(() => { - cl.x('p1'); - - return geoFirestore.remove('loc1'); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_exited\' callback fires when a location within the GeoFirestoreQuery is entirely removed from GeoFirestore', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited'); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [2, 3] - }).then(() => { - cl.x('p1'); - - return geoFirestore.remove('loc1'); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_exited\' callback properly fires when multiple keys are at the same location inside the GeoFirestoreQuery and only one of them moves outside the GeoFirestoreQuery', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited'); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [0, 0], - 'loc3': [18, -121] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set('loc1', [20, -55]); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_exited\' callback properly fires when a location inside the GeoFirestoreQuery moves somewhere outside the GeoFirestoreQuery that is already occupied by another key', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited'); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, 50], - 'loc3': [18, -121] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set('loc1', [18, -121]); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - - it('multiple \'key_exited\' callbacks fire when a location leaves the GeoFirestoreQuery', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited1', 'loc4 exited1', 'loc1 exited2', 'loc4 exited2'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited1'); - }); - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited2'); - }); - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [50, -7], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [67, 55] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [25, 90], - 'loc4': [25, 5] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - }).catch(failTestOnCaughtError); - }); - }); - - describe('\'key_*\' events combined:', () => { - it('\'key_*\' event callbacks fire when used all at the same time', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc1 moved', 'loc4 exited', 'loc1 exited', 'loc5 entered'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited'); - }); - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved'); - }); - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [50, -7], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [67, 55] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [1, 1], - 'loc4': [25, 5] - }); - }).then(() => { - cl.x('p2'); - - return geoFirestore.set({ - 'loc1': [10, -100], - 'loc2': [50, -50], - 'loc5': [5, 5] - }); - }).then(() => { - cl.x('p3'); - - return wait(100); - }).then(() => { - cl.x('p4'); - }).catch(failTestOnCaughtError); - }); - - it('location moving between geohash queries triggers a key_moved', (done) => { - const cl = new Checklist(['loc1 entered', 'loc2 entered', 'p1', 'loc1 moved', 'loc2 moved', 'p2'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [0, 0], radius: 1000 })); - - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited'); - }); - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved'); - }); - - geoFirestore.set({ - 'loc1': [-1, -1], - 'loc2': [1, 1] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [1, 1], - 'loc2': [-1, -1] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).catch(failTestOnCaughtError); - }); - }); - - describe('Cancelling GeoFirestoreQuery:', () => { - it('cancel() prevents GeoFirestoreQuery from firing any more \'key_*\' event callbacks', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 entered', 'loc4 entered', 'loc1 moved', 'loc4 exited'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited'); - }); - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved'); - }); - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [50, -7], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [67, 55] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [1, 1], - 'loc4': [25, 5] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3') - - geoFirestoreQueries[0].cancel(); - - return wait(1000); - }).then(() => { - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited'); - }); - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved'); - }); - - return geoFirestore.set({ - 'loc1': [10, -100], - 'loc2': [50, -50], - 'loc5': [5, 5] - }); - }).then(() => { - cl.x('p4'); - - return wait(100); - }).then(() => { - cl.x('p5'); - }).catch(failTestOnCaughtError); - }); - - it('Calling cancel() on one GeoFirestoreQuery does not cancel other GeoQueries', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 entered1', 'loc1 entered2', 'loc4 entered1', 'loc4 entered2', 'loc1 moved1', 'loc1 moved2', 'loc4 exited1', 'loc4 exited2', 'loc1 exited2', 'loc5 entered2'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered1'); - }); - geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited1'); - }); - geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved1'); - }); - - geoFirestoreQueries[1].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered2'); - }); - geoFirestoreQueries[1].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited2'); - }); - geoFirestoreQueries[1].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved2'); - }); - - geoFirestore.set({ - 'loc1': [2, 3], - 'loc2': [50, -7], - 'loc3': [16, -150], - 'loc4': [5, 5], - 'loc5': [67, 55] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set({ - 'loc1': [1, 1], - 'loc4': [25, 5] - }); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - geoFirestoreQueries[0].cancel(); - - return geoFirestore.set({ - 'loc1': [10, -100], - 'loc2': [50, -50], - 'loc5': [1, 2] - }); - }).then(() => { - cl.x('p4'); - - return wait(100); - }).then(() => { - cl.x('p5'); - }).catch(failTestOnCaughtError); - }); - - it('Calling cancel() in the middle of firing \'key_entered\' events is allowed', (done) => { - const cl = new Checklist(['p1', 'key entered', 'cancel query'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - geoFirestore.set({ - 'loc1': [1, 2], - 'loc2': [1, 3], - 'loc3': [1, 4] - }).then(() => { - cl.x('p1'); - - let numKeyEnteredEventsFired = 0; - geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x('key entered'); - numKeyEnteredEventsFired++; - if (numKeyEnteredEventsFired === 1) { - cl.x('cancel query'); - geoFirestoreQueries[0].cancel(); - } - }); - }).catch(failTestOnCaughtError); - }); - }); -}); diff --git a/test/tools/geoFireCallbackRegistration.test.ts b/test/tests/geoCallbackRegistration.test.ts similarity index 76% rename from test/tools/geoFireCallbackRegistration.test.ts rename to test/tests/geoCallbackRegistration.test.ts index 04a5f7a7..0983410c 100644 --- a/test/tools/geoFireCallbackRegistration.test.ts +++ b/test/tests/geoCallbackRegistration.test.ts @@ -1,7 +1,7 @@ -import { GeoCallbackRegistration } from '../../src/tools/callbackRegistration'; +import { GeoCallbackRegistration } from '../../src/geoFire/geoCallbackRegistration'; import { afterEachHelper, beforeEachHelper, Checklist, - failTestOnCaughtError, geoFire, geoFireQueries, wait + failTestOnCaughtError, geoFire, geoQueries, wait } from '../common'; import * as chai from 'chai'; @@ -33,9 +33,9 @@ describe('GeoFire GeoCallbackRegistration Tests:', () => { it('\'key_moved\' registrations can be cancelled', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 moved'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - const onKeyMovedRegistration = geoFireQueries[0].on('key_moved', (key, location, distance) => { + const onKeyMovedRegistration = geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -68,9 +68,9 @@ describe('GeoFire GeoCallbackRegistration Tests:', () => { it('\'key_entered\' registrations can be cancelled', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - const onKeyEnteredRegistration = geoFireQueries[0].on('key_entered', (key, location, distance) => { + const onKeyEnteredRegistration = geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -99,9 +99,9 @@ describe('GeoFire GeoCallbackRegistration Tests:', () => { it('\'key_exited\' registrations can be cancelled', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 exited'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - const onKeyExitedRegistration = geoFireQueries[0].on('key_exited', (key, location, distance) => { + const onKeyExitedRegistration = geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -134,12 +134,12 @@ describe('GeoFire GeoCallbackRegistration Tests:', () => { it('Cancelling a \'key_moved\' registration does not cancel all \'key_moved\' callbacks', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 moved1', 'loc1 moved2', 'loc3 moved2'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - const onKeyMovedRegistration1 = geoFireQueries[0].on('key_moved', (key, location, distance) => { + const onKeyMovedRegistration1 = geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved1'); }); - const onKeyMovedRegistration2 = geoFireQueries[0].on('key_moved', (key, location, distance) => { + const onKeyMovedRegistration2 = geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved2'); }); @@ -172,12 +172,12 @@ describe('GeoFire GeoCallbackRegistration Tests:', () => { it('Cancelling a \'key_entered\' registration does not cancel all \'key_entered\' callbacks', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered1', 'loc1 entered2', 'loc3 entered2'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - const onKeyEnteredRegistration1 = geoFireQueries[0].on('key_entered', (key, location, distance) => { + const onKeyEnteredRegistration1 = geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered1'); }); - const onKeyEnteredRegistration2 = geoFireQueries[0].on('key_entered', (key, location, distance) => { + const onKeyEnteredRegistration2 = geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered2'); }); @@ -206,12 +206,12 @@ describe('GeoFire GeoCallbackRegistration Tests:', () => { it('Cancelling a \'key_exited\' registration does not cancel all \'key_exited\' callbacks', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 exited1', 'loc1 exited2', 'loc3 exited2'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - const onKeyExitedRegistration1 = geoFireQueries[0].on('key_exited', (key, location, distance) => { + const onKeyExitedRegistration1 = geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited1'); }); - const onKeyExitedRegistration2 = geoFireQueries[0].on('key_exited', (key, location, distance) => { + const onKeyExitedRegistration2 = geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited2'); }); @@ -242,9 +242,9 @@ describe('GeoFire GeoCallbackRegistration Tests:', () => { }); it('Calling cancel on a GeoCallbackRegistration twice does not throw', () => { - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - const onKeyExitedRegistration = geoFireQueries[0].on('key_exited', () => { }); + const onKeyExitedRegistration = geoQueries[0].on('key_exited', () => { }); expect(() => onKeyExitedRegistration.cancel()).not.throw(); expect(() => onKeyExitedRegistration.cancel()).not.throw(); diff --git a/test/firebase/index.test.ts b/test/tests/geoFire.test.ts similarity index 98% rename from test/firebase/index.test.ts rename to test/tests/geoFire.test.ts index 4daf5139..edbabb50 100755 --- a/test/firebase/index.test.ts +++ b/test/tests/geoFire.test.ts @@ -1,9 +1,9 @@ import * as chai from 'chai'; -import { GeoFire } from '../../src/firebase'; -import { GeoFireQuery } from '../../src/firebase/query'; +import { GeoFire } from '../../src/geoFire'; +import { GeoQuery } from '../../src/geoFire/geoQuery'; import { - afterEachHelper, beforeEachHelper, Checklist, failTestOnCaughtError, geoFire, geoFireRef, getFirebaseData, geoFireQueries, + afterEachHelper, beforeEachHelper, Checklist, failTestOnCaughtError, geoFire, geoFireRef, getFirebaseData, geoQueries, invalidFirebaseRefs, invalidKeys, invalidLocations, invalidQueryCriterias, validKeys, validLocations, validQueryCriterias } from '../common'; @@ -822,10 +822,10 @@ describe('GeoFire Tests:', () => { }); describe('query():', () => { - it('query() returns GeoFireQuery instance', () => { - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + it('query() returns GeoQuery instance', () => { + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - expect(geoFireQueries[0] instanceof GeoFireQuery).to.be.ok; + expect(geoQueries[0] instanceof GeoQuery).to.be.ok; }); it('query() does not throw errors given valid query criteria', () => { diff --git a/test/tools/utils.test.ts b/test/tests/geoFireUtils.test.ts similarity index 99% rename from test/tools/utils.test.ts rename to test/tests/geoFireUtils.test.ts index b68546ce..278db116 100644 --- a/test/tools/utils.test.ts +++ b/test/tests/geoFireUtils.test.ts @@ -1,10 +1,10 @@ import * as chai from 'chai'; -import { GeoFire } from '../../src/firebase'; +import { GeoFire } from '../../src/geoFire'; import { boundingBoxBits, degreesToRadians, encodeGeohash, geohashQuery, geohashQueries, g_GEOHASH_PRECISION, metersToLongitudeDegrees, validateCriteria, validateGeohash, validateKey, validateLocation, wrapLongitude -} from '../../src/tools/utils'; +} from '../../src/geoFire/geoFireUtils'; import { invalidGeohashes, invalidKeys, invalidLocations, invalidQueryCriterias, validGeohashes, validKeys, validLocations, validQueryCriterias diff --git a/test/firebase/query.test.ts b/test/tests/geoQuery.test.ts similarity index 70% rename from test/firebase/query.test.ts rename to test/tests/geoQuery.test.ts index f4a3f23c..5e469828 100644 --- a/test/firebase/query.test.ts +++ b/test/tests/geoQuery.test.ts @@ -1,12 +1,12 @@ import * as chai from 'chai'; import { - afterEachHelper, beforeEachHelper, Checklist, failTestOnCaughtError, geoFire, geoFireQueries, invalidQueryCriterias, validQueryCriterias, wait + afterEachHelper, beforeEachHelper, Checklist, failTestOnCaughtError, geoFire, geoQueries, invalidQueryCriterias, validQueryCriterias, wait } from '../common'; const expect = chai.expect; -describe('GeoFireQuery Tests:', () => { +describe('GeoQuery Tests:', () => { // Reset the Firebase before each test beforeEach((done) => { beforeEachHelper(done); @@ -18,10 +18,10 @@ describe('GeoFireQuery Tests:', () => { describe('Constructor:', () => { it('Constructor stores query criteria', () => { - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - expect(geoFireQueries[0].center()).to.deep.equal([1, 2]); - expect(geoFireQueries[0].radius()).to.equal(1000); + expect(geoQueries[0].center()).to.deep.equal([1, 2]); + expect(geoQueries[0].radius()).to.equal(1000); }); it('Constructor throws error on invalid query criteria', () => { @@ -56,46 +56,46 @@ describe('GeoFireQuery Tests:', () => { describe('updateCriteria():', () => { it('updateCriteria() updates query criteria', () => { - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - expect(geoFireQueries[0].center()).to.deep.equal([1, 2]); - expect(geoFireQueries[0].radius()).to.equal(1000); + expect(geoQueries[0].center()).to.deep.equal([1, 2]); + expect(geoQueries[0].radius()).to.equal(1000); - geoFireQueries[0].updateCriteria({ center: [2, 3], radius: 100 }); + geoQueries[0].updateCriteria({ center: [2, 3], radius: 100 }); - expect(geoFireQueries[0].center()).to.deep.equal([2, 3]); - expect(geoFireQueries[0].radius()).to.equal(100); + expect(geoQueries[0].center()).to.deep.equal([2, 3]); + expect(geoQueries[0].radius()).to.equal(100); }); it('updateCriteria() updates query criteria when given only center', () => { - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - expect(geoFireQueries[0].center()).to.deep.equal([1, 2]); - expect(geoFireQueries[0].radius()).to.equal(1000); + expect(geoQueries[0].center()).to.deep.equal([1, 2]); + expect(geoQueries[0].radius()).to.equal(1000); - geoFireQueries[0].updateCriteria({ center: [2, 3] }); + geoQueries[0].updateCriteria({ center: [2, 3] }); - expect(geoFireQueries[0].center()).to.deep.equal([2, 3]); - expect(geoFireQueries[0].radius()).to.equal(1000); + expect(geoQueries[0].center()).to.deep.equal([2, 3]); + expect(geoQueries[0].radius()).to.equal(1000); }); it('updateCriteria() updates query criteria when given only radius', () => { - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - expect(geoFireQueries[0].center()).to.deep.equal([1, 2]); - expect(geoFireQueries[0].radius()).to.equal(1000); + expect(geoQueries[0].center()).to.deep.equal([1, 2]); + expect(geoQueries[0].radius()).to.equal(1000); - geoFireQueries[0].updateCriteria({ radius: 100 }); + geoQueries[0].updateCriteria({ radius: 100 }); - expect(geoFireQueries[0].center()).to.deep.equal([1, 2]); - expect(geoFireQueries[0].radius()).to.equal(100); + expect(geoQueries[0].center()).to.deep.equal([1, 2]); + expect(geoQueries[0].radius()).to.equal(100); }); - it('updateCriteria() fires \'key_entered\' callback for locations which now belong to the GeoFireQuery', (done) => { + it('updateCriteria() fires \'key_entered\' callback for locations which now belong to the GeoQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); - geoFireQueries.push(geoFire.query({ center: [90, 90], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries.push(geoFire.query({ center: [90, 90], radius: 1000 })); + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -108,7 +108,7 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoFireQueries[0].updateCriteria({ center: [1, 2], radius: 1000 }); + geoQueries[0].updateCriteria({ center: [1, 2], radius: 1000 }); return wait(100); }).then(() => { @@ -116,11 +116,11 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('updateCriteria() fires \'key_entered\' callback for locations with complex keys which now belong to the GeoFireQuery', (done) => { + it('updateCriteria() fires \'key_entered\' callback for locations with complex keys which now belong to the GeoQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'loc:^:*1 entered', 'loc-+-+-4 entered'], expect, done); - geoFireQueries.push(geoFire.query({ center: [90, 90], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries.push(geoFire.query({ center: [90, 90], radius: 1000 })); + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -133,7 +133,7 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoFireQueries[0].updateCriteria({ center: [1, 2], radius: 1000 }); + geoQueries[0].updateCriteria({ center: [1, 2], radius: 1000 }); return wait(100); }).then(() => { @@ -141,11 +141,11 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('updateCriteria() fires \'key_exited\' callback for locations which no longer belong to the GeoFireQuery', (done) => { + it('updateCriteria() fires \'key_exited\' callback for locations which no longer belong to the GeoQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 exited', 'loc4 exited'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -158,7 +158,7 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoFireQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); + geoQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); return wait(100); }).then(() => { @@ -169,11 +169,11 @@ describe('GeoFireQuery Tests:', () => { it('updateCriteria() does not cause event callbacks to fire on the previous criteria', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc1 exited', 'loc4 exited', 'loc4 entered', 'loc5 entered'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -186,7 +186,7 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoFireQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); + geoQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); return wait(100); }).then(() => { @@ -208,14 +208,14 @@ describe('GeoFireQuery Tests:', () => { it('updateCriteria() does not cause \'key_moved\' callbacks to fire for keys in both the previous and updated queries', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc4 exited', 'loc2 entered'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -228,7 +228,7 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoFireQueries[0].updateCriteria({ center: [1, 1], radius: 1000 }); + geoQueries[0].updateCriteria({ center: [1, 1], radius: 1000 }); return wait(100); }).then(() => { @@ -250,14 +250,14 @@ describe('GeoFireQuery Tests:', () => { it('updateCriteria() does not cause \'key_exited\' callbacks to fire twice for keys in the previous query but not in the updated query and which were moved after the query was updated', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'loc1 entered', 'loc4 entered', 'loc1 exited', 'loc4 exited', 'loc4 entered', 'loc5 entered', 'loc5 moved'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -271,7 +271,7 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoFireQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); + geoQueries[0].updateCriteria({ center: [90, 90], radius: 1000 }); return wait(100); }).then(() => { @@ -302,40 +302,40 @@ describe('GeoFireQuery Tests:', () => { }); it('updateCriteria() does not throw errors given valid query criteria', () => { - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); validQueryCriterias.forEach((validQueryCriteria) => { - expect(() => geoFireQueries[0].updateCriteria(validQueryCriteria)).not.to.throw(); + expect(() => geoQueries[0].updateCriteria(validQueryCriteria)).not.to.throw(); }); }); it('updateCriteria() throws errors given invalid query criteria', () => { - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); invalidQueryCriterias.forEach((invalidQueryCriteria) => { // @ts-ignore - expect(() => geoFireQueries[0].updateCriteria(invalidQueryCriteria)).to.throw(); + expect(() => geoQueries[0].updateCriteria(invalidQueryCriteria)).to.throw(); }); }); }); describe('on():', () => { it('on() throws error given invalid event type', () => { - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); const setInvalidEventType = () => { - geoFireQueries[0].on('invalid_event', () => { }); + geoQueries[0].on('invalid_event', () => { }); } expect(setInvalidEventType).to.throw(); }); it('on() throws error given invalid callback', () => { - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); const setInvalidCallback = () => { // @ts-ignore - geoFireQueries[0].on('key_entered', 'non-function'); + geoQueries[0].on('key_entered', 'non-function'); } expect(setInvalidCallback).to.throw(); @@ -360,13 +360,13 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoFireQueries[0].on('ready', () => { + geoQueries[0].on('ready', () => { expect(cl.length()).to.be.equal(1); cl.x('ready fired'); }); @@ -390,16 +390,16 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoFireQueries[0].on('ready', () => { + geoQueries[0].on('ready', () => { expect(cl.length()).to.be.equal(2); cl.x('ready1 fired'); - geoFireQueries[0].on('ready', () => { + geoQueries[0].on('ready', () => { expect(cl.length()).to.be.equal(1); cl.x('ready2 fired'); }); @@ -409,14 +409,14 @@ describe('GeoFireQuery Tests:', () => { it('\'ready\' event fires after increasing the query radius, even if no new geohashes were queried', (done) => { const cl = new Checklist(['ready1 fired', 'ready2 fired'], expect, done); - geoFireQueries.push(geoFire.query({ center: [37.7851382, -122.405893], radius: 6 })); - const onReadyCallbackRegistration1 = geoFireQueries[0].on('ready', () => { + geoQueries.push(geoFire.query({ center: [37.7851382, -122.405893], radius: 6 })); + const onReadyCallbackRegistration1 = geoQueries[0].on('ready', () => { cl.x('ready1 fired'); onReadyCallbackRegistration1.cancel(); - geoFireQueries[0].updateCriteria({ + geoQueries[0].updateCriteria({ radius: 7 }); - geoFireQueries[0].on('ready', () => { + geoQueries[0].on('ready', () => { cl.x('ready2 fired'); }); }); @@ -434,27 +434,27 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - const onReadyCallbackRegistration1 = geoFireQueries[0].on('ready', () => { + const onReadyCallbackRegistration1 = geoQueries[0].on('ready', () => { expect(cl.length()).to.be.equal(6); cl.x('ready1 fired'); onReadyCallbackRegistration1.cancel(); - geoFireQueries[0].updateCriteria({ + geoQueries[0].updateCriteria({ center: [51, 51] }); - geoFireQueries[0].on('ready', () => { + geoQueries[0].on('ready', () => { expect(cl.length()).to.be.equal(1); cl.x('ready2 fired'); }); @@ -464,12 +464,12 @@ describe('GeoFireQuery Tests:', () => { }); describe('\'key_moved\' event:', () => { - it('\'key_moved\' callback does not fire for brand new locations within or outside of the GeoFireQuery', (done) => { + it('\'key_moved\' callback does not fire for brand new locations within or outside of the GeoQuery', (done) => { const cl = new Checklist(['p1', 'p2'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -486,12 +486,12 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback does not fire for locations outside of the GeoFireQuery which are moved somewhere else outside of the GeoFireQuery', (done) => { + it('\'key_moved\' callback does not fire for locations outside of the GeoQuery which are moved somewhere else outside of the GeoQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -515,12 +515,12 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback does not fire for locations outside of the GeoFireQuery which are moved within the GeoFireQuery', (done) => { + it('\'key_moved\' callback does not fire for locations outside of the GeoQuery which are moved within the GeoQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -544,12 +544,12 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback does not fire for locations within the GeoFireQuery which are moved somewhere outside of the GeoFireQuery', (done) => { + it('\'key_moved\' callback does not fire for locations within the GeoQuery which are moved somewhere outside of the GeoQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -573,12 +573,12 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback does not fires for a location within the GeoFireQuery which is set to the same location', (done) => { + it('\'key_moved\' callback does not fires for a location within the GeoQuery which is set to the same location', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc3 moved'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -603,12 +603,12 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback fires for locations within the GeoFireQuery which are moved somewhere else within the GeoFireQuery', (done) => { + it('\'key_moved\' callback fires for locations within the GeoQuery which are moved somewhere else within the GeoQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -635,9 +635,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_moved\' callback gets passed correct location parameter', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved to 2,2', 'loc3 moved to -1,-1'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved to ' + location); }); @@ -664,9 +664,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_moved\' callback gets passed correct distance parameter', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved (111.19 km from center)', 'loc3 moved (400.90 km from center)'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved (' + distance.toFixed(2) + ' km from center)'); }); @@ -690,12 +690,12 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback properly fires when multiple keys are at the same location within the GeoFireQuery and only one of them moves somewhere else within the GeoFireQuery', (done) => { + it('\'key_moved\' callback properly fires when multiple keys are at the same location within the GeoQuery and only one of them moves somewhere else within the GeoQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -719,12 +719,12 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_moved\' callback properly fires when a location within the GeoFireQuery moves somehwere else within the GeoFireQuery that is already occupied by another key', (done) => { + it('\'key_moved\' callback properly fires when a location within the GeoQuery moves somehwere else within the GeoQuery that is already occupied by another key', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved', 'loc3 moved'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -748,15 +748,15 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('multiple \'key_moved\' callbacks fire for locations within the GeoFireQuery which are moved somewhere else within the GeoFireQuery', (done) => { + it('multiple \'key_moved\' callbacks fire for locations within the GeoQuery which are moved somewhere else within the GeoQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 moved1', 'loc3 moved1', 'loc1 moved2', 'loc3 moved2'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved1'); }); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved2'); }); @@ -782,10 +782,10 @@ describe('GeoFireQuery Tests:', () => { }); describe('\'key_entered\' event:', () => { - it('\'key_entered\' callback fires when a location enters the GeoFireQuery before onKeyEntered() was called', (done) => { + it('\'key_entered\' callback fires when a location enters the GeoQuery before onKeyEntered() was called', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); geoFire.set({ 'loc1': [2, 3], @@ -796,7 +796,7 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p1'); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -806,12 +806,12 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_entered\' callback fires when a location enters the GeoFireQuery after onKeyEntered() was called', (done) => { + it('\'key_entered\' callback fires when a location enters the GeoQuery after onKeyEntered() was called', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 entered', 'loc4 entered'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -833,9 +833,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_entered\' callback gets passed correct location parameter', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 entered at 2,3', 'loc4 entered at 5,5'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered at ' + location); }); @@ -857,9 +857,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_entered\' callback gets passed correct distance parameter', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 entered (157.23 km from center)', 'loc4 entered (555.66 km from center)'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered (' + distance.toFixed(2) + ' km from center)'); }); @@ -878,12 +878,12 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_entered\' callback properly fires when multiple keys are at the same location outside the GeoFireQuery and only one of them moves within the GeoFireQuery', (done) => { + it('\'key_entered\' callback properly fires when multiple keys are at the same location outside the GeoQuery and only one of them moves within the GeoQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 entered'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -904,12 +904,12 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_entered\' callback properly fires when a location outside the GeoFireQuery moves somewhere within the GeoFireQuery that is already occupied by another key', (done) => { + it('\'key_entered\' callback properly fires when a location outside the GeoQuery moves somewhere within the GeoQuery that is already occupied by another key', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 entered', 'loc3 entered'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); @@ -930,15 +930,15 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('multiple \'key_entered\' callbacks fire when a location enters the GeoFireQuery', (done) => { + it('multiple \'key_entered\' callbacks fire when a location enters the GeoQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'loc1 entered1', 'loc4 entered1', 'loc1 entered2', 'loc4 entered2'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered1'); }); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered2'); }); @@ -959,12 +959,12 @@ describe('GeoFireQuery Tests:', () => { }); describe('\'key_exited\' event:', () => { - it('\'key_exited\' callback fires when a location leaves the GeoFireQuery', (done) => { + it('\'key_exited\' callback fires when a location leaves the GeoQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited', 'loc4 exited'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -993,9 +993,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_exited\' callback gets passed correct location parameter', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited to 25,90', 'loc4 exited to 25,5'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited to ' + location); }); @@ -1025,9 +1025,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_exited\' callback gets passed correct distance parameter', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited (9759.01 km from center)', 'loc4 exited (2688.06 km from center)'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited (' + distance.toFixed(2) + ' km from center)'); }); @@ -1057,9 +1057,9 @@ describe('GeoFireQuery Tests:', () => { it('\'key_exited\' callback gets passed null for location and distance parameters if the key is entirely removed from GeoFire', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { expect(location).to.be.equal(null); expect(distance).to.be.equal(null); cl.x(key + ' exited'); @@ -1078,12 +1078,12 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_exited\' callback fires when a location within the GeoFireQuery is entirely removed from GeoFire', (done) => { + it('\'key_exited\' callback fires when a location within the GeoQuery is entirely removed from GeoFire', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -1103,11 +1103,11 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_exited\' callback properly fires when multiple keys are at the same location inside the GeoFireQuery and only one of them moves outside the GeoFireQuery', (done) => { + it('\'key_exited\' callback properly fires when multiple keys are at the same location inside the GeoQuery and only one of them moves outside the GeoQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -1128,12 +1128,12 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('\'key_exited\' callback properly fires when a location inside the GeoFireQuery moves somewhere outside the GeoFireQuery that is already occupied by another key', (done) => { + it('\'key_exited\' callback properly fires when a location inside the GeoQuery moves somewhere outside the GeoQuery that is already occupied by another key', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); @@ -1154,15 +1154,15 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('multiple \'key_exited\' callbacks fire when a location leaves the GeoFireQuery', (done) => { + it('multiple \'key_exited\' callbacks fire when a location leaves the GeoQuery', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'loc1 exited1', 'loc4 exited1', 'loc1 exited2', 'loc4 exited2'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited1'); }); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited2'); }); @@ -1193,15 +1193,15 @@ describe('GeoFireQuery Tests:', () => { it('\'key_*\' event callbacks fire when used all at the same time', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered', 'loc4 entered', 'loc1 moved', 'loc4 exited', 'loc1 exited', 'loc5 entered'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -1238,15 +1238,15 @@ describe('GeoFireQuery Tests:', () => { it('location moving between geohash queries triggers a key_moved', (done) => { const cl = new Checklist(['loc1 entered', 'loc2 entered', 'p1', 'loc1 moved', 'loc2 moved', 'p2'], expect, done); - geoFireQueries.push(geoFire.query({ center: [0, 0], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [0, 0], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -1268,19 +1268,19 @@ describe('GeoFireQuery Tests:', () => { }); }); - describe('Cancelling GeoFireQuery:', () => { - it('cancel() prevents GeoFireQuery from firing any more \'key_*\' event callbacks', (done) => { + describe('Cancelling GeoQuery:', () => { + it('cancel() prevents GeoQuery from firing any more \'key_*\' event callbacks', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 entered', 'loc4 entered', 'loc1 moved', 'loc4 exited'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -1304,17 +1304,17 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p3') - geoFireQueries[0].cancel(); + geoQueries[0].cancel(); return wait(1000); }).then(() => { - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered'); }); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited'); }); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved'); }); @@ -1332,29 +1332,29 @@ describe('GeoFireQuery Tests:', () => { }).catch(failTestOnCaughtError); }); - it('Calling cancel() on one GeoFireQuery does not cancel other geoFireQueries', (done) => { + it('Calling cancel() on one GeoQuery does not cancel other geoQueries', (done) => { const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 entered1', 'loc1 entered2', 'loc4 entered1', 'loc4 entered2', 'loc1 moved1', 'loc1 moved2', 'loc4 exited1', 'loc4 exited2', 'loc1 exited2', 'loc5 entered2'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x(key + ' entered1'); }); - geoFireQueries[0].on('key_exited', (key, location, distance) => { + geoQueries[0].on('key_exited', (key, location, distance) => { cl.x(key + ' exited1'); }); - geoFireQueries[0].on('key_moved', (key, location, distance) => { + geoQueries[0].on('key_moved', (key, location, distance) => { cl.x(key + ' moved1'); }); - geoFireQueries[1].on('key_entered', (key, location, distance) => { + geoQueries[1].on('key_entered', (key, location, distance) => { cl.x(key + ' entered2'); }); - geoFireQueries[1].on('key_exited', (key, location, distance) => { + geoQueries[1].on('key_exited', (key, location, distance) => { cl.x(key + ' exited2'); }); - geoFireQueries[1].on('key_moved', (key, location, distance) => { + geoQueries[1].on('key_moved', (key, location, distance) => { cl.x(key + ' moved2'); }); @@ -1378,7 +1378,7 @@ describe('GeoFireQuery Tests:', () => { }).then(() => { cl.x('p3') - geoFireQueries[0].cancel(); + geoQueries[0].cancel(); return geoFire.set({ 'loc1': [10, -100], @@ -1397,7 +1397,7 @@ describe('GeoFireQuery Tests:', () => { it('Calling cancel() in the middle of firing \'key_entered\' events is allowed', (done) => { const cl = new Checklist(['p1', 'key entered', 'cancel query'], expect, done); - geoFireQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); + geoQueries.push(geoFire.query({ center: [1, 2], radius: 1000 })); geoFire.set({ 'loc1': [1, 2], @@ -1407,12 +1407,12 @@ describe('GeoFireQuery Tests:', () => { cl.x('p1'); let numKeyEnteredEventsFired = 0; - geoFireQueries[0].on('key_entered', (key, location, distance) => { + geoQueries[0].on('key_entered', (key, location, distance) => { cl.x('key entered'); numKeyEnteredEventsFired++; if (numKeyEnteredEventsFired === 1) { cl.x('cancel query'); - geoFireQueries[0].cancel(); + geoQueries[0].cancel(); } }); }).catch(failTestOnCaughtError); diff --git a/test/tools/geoFirestoreCallbackRegistration.test.ts b/test/tools/geoFirestoreCallbackRegistration.test.ts deleted file mode 100644 index cc08f316..00000000 --- a/test/tools/geoFirestoreCallbackRegistration.test.ts +++ /dev/null @@ -1,253 +0,0 @@ -import { GeoCallbackRegistration } from '../../src/tools/callbackRegistration'; -import { - afterEachHelperFirestore, beforeEachHelperFirestore, Checklist, - failTestOnCaughtError, geoFirestore, geoFirestoreQueries, wait -} from '../common'; - -import * as chai from 'chai'; - -const expect = chai.expect; - -describe('GeoFirestore GeoCallbackRegistration Tests:', () => { - // Reset the Firestore before each test - beforeEach((done) => { - beforeEachHelperFirestore(done); - }); - - afterEach((done) => { - afterEachHelperFirestore(done); - }); - - describe('Constructor:', () => { - it('Constructor throws error given non-function', () => { - const createCallbackRegistration = () => { - // @ts-ignore - new GeoCallbackRegistration('nonFunction'); - } - - expect(() => createCallbackRegistration()).to.throw(null, 'callback must be a function'); - }); - }); - - describe('Cancelling event callbacks:', () => { - it('\'key_moved\' registrations can be cancelled', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 moved'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - const onKeyMovedRegistration = geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved'); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, -7], - 'loc3': [1, 1] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set('loc1', [2, 2]); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - onKeyMovedRegistration.cancel(); - cl.x('p3'); - - return geoFirestore.set('loc3', [1, 2]); - }).then(() => { - cl.x('p4'); - - return wait(100); - }).then(() => { - cl.x('p5'); - }).catch(failTestOnCaughtError);; - }); - - it('\'key_entered\' registrations can be cancelled', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - const onKeyEnteredRegistration = geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered'); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, -7], - 'loc3': [80, 80] - }).then(() => { - cl.x('p1'); - - return wait(100); - }).then(() => { - onKeyEnteredRegistration.cancel(); - cl.x('p2'); - - return geoFirestore.set('loc3', [1, 2]); - }).then(() => { - cl.x('p3'); - - return wait(100); - }).then(() => { - cl.x('p4'); - }).catch(failTestOnCaughtError); - }); - - it('\'key_exited\' registrations can be cancelled', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 exited'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - const onKeyExitedRegistration = geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited'); - onKeyExitedRegistration.cancel(); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, -7], - 'loc3': [1, 1] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set('loc1', [80, 80]); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - cl.x('p3'); - - return geoFirestore.set('loc3', [-80, -80]); - }).then(() => { - cl.x('p4'); - - return wait(100); - }).then(() => { - cl.x('p5'); - }).catch(failTestOnCaughtError); - }); - - it('Cancelling a \'key_moved\' registration does not cancel all \'key_moved\' callbacks', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 moved1', 'loc1 moved2', 'loc3 moved2'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - const onKeyMovedRegistration1 = geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved1'); - }); - const onKeyMovedRegistration2 = geoFirestoreQueries[0].on('key_moved', (key, location, distance) => { - cl.x(key + ' moved2'); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, -7], - 'loc3': [1, 1] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set('loc1', [2, 2]); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - onKeyMovedRegistration1.cancel(); - cl.x('p3'); - - return geoFirestore.set('loc3', [1, 2]); - }).then(() => { - cl.x('p4'); - - return wait(100); - }).then(() => { - cl.x('p5'); - }).catch(failTestOnCaughtError); - }); - - it('Cancelling a \'key_entered\' registration does not cancel all \'key_entered\' callbacks', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'loc1 entered1', 'loc1 entered2', 'loc3 entered2'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - const onKeyEnteredRegistration1 = geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered1'); - }); - const onKeyEnteredRegistration2 = geoFirestoreQueries[0].on('key_entered', (key, location, distance) => { - cl.x(key + ' entered2'); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, -7], - 'loc3': [80, 80] - }).then(() => { - cl.x('p1'); - - return wait(100); - }).then(() => { - onKeyEnteredRegistration1.cancel(); - cl.x('p2'); - - return geoFirestore.set('loc3', [1, 2]); - }).then(() => { - cl.x('p3'); - - return wait(100); - }).then(() => { - cl.x('p4'); - }).catch(failTestOnCaughtError); - }); - - it('Cancelling a \'key_exited\' registration does not cancel all \'key_exited\' callbacks', (done) => { - const cl = new Checklist(['p1', 'p2', 'p3', 'p4', 'p5', 'loc1 exited1', 'loc1 exited2', 'loc3 exited2'], expect, done); - - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - const onKeyExitedRegistration1 = geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited1'); - }); - const onKeyExitedRegistration2 = geoFirestoreQueries[0].on('key_exited', (key, location, distance) => { - cl.x(key + ' exited2'); - }); - - geoFirestore.set({ - 'loc1': [0, 0], - 'loc2': [50, -7], - 'loc3': [1, 1] - }).then(() => { - cl.x('p1'); - - return geoFirestore.set('loc1', [80, 80]); - }).then(() => { - cl.x('p2'); - - return wait(100); - }).then(() => { - onKeyExitedRegistration1.cancel(); - cl.x('p3'); - - return geoFirestore.set('loc3', [-80, -80]); - }).then(() => { - cl.x('p4'); - - return wait(100); - }).then(() => { - cl.x('p5'); - }).catch(failTestOnCaughtError); - }); - - it('Calling cancel on a GeoCallbackRegistration twice does not throw', () => { - geoFirestoreQueries.push(geoFirestore.query({ center: [1, 2], radius: 1000 })); - - const onKeyExitedRegistration = geoFirestoreQueries[0].on('key_exited', () => { }); - - expect(() => onKeyExitedRegistration.cancel()).not.throw(); - expect(() => onKeyExitedRegistration.cancel()).not.throw(); - }); - }); -}); From 6b7ca8a57d1e336f4fc0c4c9f295f805705c8ada Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Sun, 6 May 2018 13:39:29 -0700 Subject: [PATCH 17/23] build: migrate build process to rollup --- .gitignore | 3 +- .travis.yml | 1 - package-lock.json | 4916 ++++++++++++++++++++++++--------------------- package.json | 20 +- rollup.config.js | 48 + src/index.ts | 2 + tsconfig.json | 22 +- 7 files changed, 2661 insertions(+), 2351 deletions(-) create mode 100644 rollup.config.js diff --git a/.gitignore b/.gitignore index 7ab08609..29d7970d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ dist/ node_modules/ coverage/ .DS_Store -.nyc_output/ \ No newline at end of file +.nyc_output/ +.rpt2_cache \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index a1f9a0dd..c777e1a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,4 +8,3 @@ script: - npm run test after_success: - npm run coverage - - npm run build diff --git a/package-lock.json b/package-lock.json index 7b75d10f..d84ced14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,136 +5,176 @@ "requires": true, "dependencies": { "@firebase/app": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.1.10.tgz", - "integrity": "sha512-2GTXt3b2QZXkmx6/5nNJq+pEN/VTjAG55MFJS1WMoLVZkwKuNpWNk65QVyPaoL88x1iHtuLqAMFgJUOnhOg+Pw==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.2.0.tgz", + "integrity": "sha512-hu+HGRXER3URCb8sZdd4f3QzvVWfuKvJoqd1mlEx9vWQ0fMpFqMeYgOaO7+x7PkNzamMHetItfws5TK9pmnStQ==", "dev": true, "requires": { - "@firebase/app-types": "0.1.2", - "@firebase/util": "0.1.10", + "@firebase/app-types": "0.2.0", + "@firebase/util": "0.1.11", "tslib": "1.9.0" } }, "@firebase/app-types": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.1.2.tgz", - "integrity": "sha512-bCIZGeMtP0ibrXNNaU214/1tRNw0jHnir/cfiAao1gjUyIS7RzOTQoH+zbwPJNEwUqJ0T3ykw/Tv4/khGqbVBg==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.2.0.tgz", + "integrity": "sha512-0hTbuFmTr5sBvn9r2w5nk9FeMkuscj+ClPUBimtOkOnoLvIWRICILRiPTV1CX+7Th4EJ7y17UWx8jieY0f8GtA==", "dev": true }, "@firebase/auth": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.3.4.tgz", - "integrity": "sha512-lpKpPGVyNEuQasukVgxrti/GptEZDE24B/UnRmvjiwpVlOpVPLsaNJkklLiODlH7DS3yIyGHWYqojNl3iaTEmA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.4.2.tgz", + "integrity": "sha512-gMEKuKDdO2oB9c2zmj6NlFOY1ynzummXuUWz6cFgUdNd/Pw5ZpkOlGzS7yxZ7NI0dZXV/xccamN7CN9/vol+cA==", "dev": true, "requires": { - "@firebase/auth-types": "0.1.2" + "@firebase/auth-types": "0.2.1" } }, "@firebase/auth-types": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.1.2.tgz", - "integrity": "sha512-pofZTXrz/urWmH+5opF4jpuv6GEaWOQtX9dl4AKAjOYoLceRyJn4OEeZodsDYdp6kLyARH1mcYtFMyZ9jvUtYg==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.2.1.tgz", + "integrity": "sha512-zjObje4RYJ7RJHt1z8A6xGnfLw/5RwUvk7AHrYwS4N3Mq17j/ZumBvYlAQz4k8sNe9GFDmjmHa30ezFWOglLEQ==", "dev": true }, "@firebase/database": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.1.11.tgz", - "integrity": "sha512-1TX8YlL3BCjgsbe1qvgg/r0SxOvIlJdmaktXXm6fnaPCjSD0Vhm5ln2EX3xGY97ft/Lruy9AA/7TfnAKIYA/+w==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.2.2.tgz", + "integrity": "sha512-iTNEN33D3V0hAG2hdx+guFBXaN4hcFS2k2EGp/bzNviAG7n2AotMscdbkS6xDS2e3Uk2/D3lfibHQO4zgJ3LIg==", "dev": true, "requires": { - "@firebase/database-types": "0.1.2", - "@firebase/util": "0.1.10", + "@firebase/database-types": "0.2.1", + "@firebase/logger": "0.1.1", + "@firebase/util": "0.1.11", "faye-websocket": "0.11.1", "tslib": "1.9.0" } }, "@firebase/database-types": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.1.2.tgz", - "integrity": "sha512-WDqyclInvbD6qNtbVlatzXW6SpsV8V1Nz7DaTM8z27CwxRtXakAXxERPnGipA0ZbZV4v+Xs1+6wC7Xc6T4EVaw==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.2.1.tgz", + "integrity": "sha512-LyvTpLImnhSTyHfPGcBxhD0tHw+R7FUb+als23Ad5hPCcGxlRgLhA+ukrhFIGA8Mt8FYHWgFm7TCX4YDRDxK6w==", "dev": true }, "@firebase/firestore": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-0.3.4.tgz", - "integrity": "sha512-dASy7mtwXmV1beFmMciuInqkvICV5ImwvnDwt/5tMuQvqSwuCZwPEkU0Tkrht92PgnTkuhu/nnZbI624Li/VXA==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-0.4.1.tgz", + "integrity": "sha512-RsceKkGQyiTJq/9odss6v+6KgJVLr5H8hgVO/u+d1Is20cQXJFpDjrgcoGairVb4S/jav2LV1/bHXj3HVLObvw==", "dev": true, "requires": { - "@firebase/firestore-types": "0.2.2", - "@firebase/webchannel-wrapper": "0.2.6", - "grpc": "1.9.1", + "@firebase/firestore-types": "0.3.0", + "@firebase/logger": "0.1.1", + "@firebase/webchannel-wrapper": "0.2.8", + "grpc": "1.10.1", "tslib": "1.9.0" } }, "@firebase/firestore-types": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-0.2.2.tgz", - "integrity": "sha512-yuC07Zi8p0myCQoU62O0fnGcNEcWZnKEGcQ1tj71Qh3E3Dw7qPJ75kXeeL95Bh1PHI0+TqAcDTEb9yVG9xIUVw==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-0.3.0.tgz", + "integrity": "sha512-pWqIALmvp91ELeFiiAOIDVvcCxkLxXffp5KYNL1I/mNgqe2CyGoNB6+pUb/S+FH3oCh/HWHBW+0lXDszcP42aA==", + "dev": true + }, + "@firebase/functions": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.1.1.tgz", + "integrity": "sha512-olSryAPNcH47WFCUmooNgO4P61dmlUEPVYOa0urTLiQh+8M8E7RuDWcqxxxiPk4BCN/l2ZeO0uCDsrzBMX4OqA==", + "dev": true, + "requires": { + "@firebase/functions-types": "0.1.1", + "@firebase/messaging-types": "0.1.3", + "isomorphic-fetch": "2.2.1" + } + }, + "@firebase/functions-types": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.1.1.tgz", + "integrity": "sha512-DMCQAuSafYChef2Wa4yYNPeToEDggnlaUHvseKxPzUwOVVmPdr9dxmqQW+UKeGYeUHfiTWFzvsrk8u1ifyvTZA==", + "dev": true + }, + "@firebase/logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.1.1.tgz", + "integrity": "sha512-5jn3HHbEfdOwychyIEIkP1cik+MW/vvoOavTOzwDkH+fv6Bx+HBUOzh09M7sCYzXFtKzjbUax9+g39mJNBLklQ==", "dev": true }, "@firebase/messaging": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.2.1.tgz", - "integrity": "sha512-r0kPCLvck5tqt1fygUxtjB/HiMv4uNcilfUmHqUOHhZT3G5NOSe7u3wVhc286jI70QyD/uJ0PsF3NzjdRA8v3g==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.2.4.tgz", + "integrity": "sha512-bgnIhKETgA8ZgWXFmF0+RLqr93/nxqnncRndxNcKTPyq4syah703NeTtaJCMR3LWrfKGV9blpEBogtX3JmJK1g==", "dev": true, "requires": { - "@firebase/messaging-types": "0.1.2", - "@firebase/util": "0.1.10", - "lcov-result-merger": "2.0.0", + "@firebase/messaging-types": "0.1.3", + "@firebase/util": "0.1.11", "tslib": "1.9.0" } }, "@firebase/messaging-types": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@firebase/messaging-types/-/messaging-types-0.1.2.tgz", - "integrity": "sha512-4Oycm2JiDaLp9jUy4O25gD/B9Hqdy11hGjSNE0rzhVox5d0e1RF08QCwVt9xpjtBLRgEpPLyD9dPeSu4YK0Y4Q==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@firebase/messaging-types/-/messaging-types-0.1.3.tgz", + "integrity": "sha512-kDixnQujUuwvqc12iFLd6ygb+cdCCXCNeJpQD7jV0mnYMbE1Sp4Y7sl2G7N7r0kcoJPHUQHERBkZlw2N3xyJCw==", "dev": true }, "@firebase/polyfill": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.1.6.tgz", - "integrity": "sha512-O58NFa/x+8peu6zZUAFKAZildlOTStIPtDClPKOeX8OIS20OH+JbMalTZ6pKqUqRbGGA52/6Ta8epgvN99+HRA==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.3.1.tgz", + "integrity": "sha512-TkmIU4OZeE+rZoQFvq9qYsHv3IYeWahvPUTknUiqN14XJ1777ZEZ+oHBM116/R/Ej3kA5EZxhahqlRHOOD/Dwg==", "dev": true, "requires": { - "promise-polyfill": "7.1.0", - "tslib": "1.9.0" + "core-js": "2.5.5", + "promise-polyfill": "7.1.2", + "whatwg-fetch": "2.0.4" } }, "@firebase/storage": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.1.8.tgz", - "integrity": "sha512-g0xYwJbgOuAaAJy5iAoEymS77m3oVqFh9IAF3A4LvqOC9q3v3ubSSYjpNHRPZstO68pMDKsNrqb2TcJgx92kSA==", + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.1.9.tgz", + "integrity": "sha512-CrlreoXIlWNAAHnOAlP0KpZ2aM8s4QFOcrIhBE3f7vE6h30TuBuhFFra4slUykCuUflyVjBYlea6ph0fJsJr0w==", "dev": true, "requires": { - "@firebase/storage-types": "0.1.2", + "@firebase/storage-types": "0.1.3", "tslib": "1.9.0" } }, "@firebase/storage-types": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.1.2.tgz", - "integrity": "sha512-/nL93m2lIqzx4FajVnskn2YTDEj0ym53LCZegZpAPxm4GIkOQ8UhzzfHFfHJJCygb58xRszDkDuRlpJlakO4pA==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.1.3.tgz", + "integrity": "sha512-xM939ObH7MLDhhrEi/UyEDj87b3AGypGp5YPpEOwvtL2KpF7VQf7g715LoaWmhGd4+e1MHFMCktQbejkpkj63A==", "dev": true }, "@firebase/util": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.1.10.tgz", - "integrity": "sha512-XEogRfUQBZ4T37TMq/3ZbuiTdRAKX8hF3TgJglUZNCJf/6QnQ+jlupCuMAXBqCGfw2Mw0m2matoCUBWpsyevOA==", + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.1.11.tgz", + "integrity": "sha512-xUMugOJBSKVKOjrKJIVeIr4Z/6iDxSuOlOJRdz0xsOBJ9+lZVxGZs0U4oZmszWhQER1zzR+EQWIYFYePt6/QMQ==", "dev": true, "requires": { "tslib": "1.9.0" } }, "@firebase/webchannel-wrapper": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.2.6.tgz", - "integrity": "sha512-Uv9ieuHVogIOOzpGmdjV3/0asMJPdssq2vrOYJ/UTlvekT6aGdv+sx2WWvIrGRWfFxWIkOxCqpqaGMYbhc88Pg==", + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.2.8.tgz", + "integrity": "sha512-ToJbeJnxDc3O325FvcKVb3yHO1hvgHjCFvhKol6Z17GiB7vL104POjFQT4RnlLiAGSRCBAMxinDec9y9vQYdyg==", "dev": true }, + "@types/acorn": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.3.tgz", + "integrity": "sha512-gou/kWQkGPMZjdCKNZGDpqxLm9+ErG/pFZKPX4tvCjr0Xf4FCYYX3nAsu7aDVKJV3KUe27+mvqqyWT/9VZoM/A==", + "dev": true, + "requires": { + "@types/estree": "0.0.39" + } + }, "@types/chai": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.2.tgz", - "integrity": "sha512-D8uQwKYUw2KESkorZ27ykzXgvkDJYXVEihGklgfp5I4HUP8D6IxtcdLTMB1emjQiWzV7WZ5ihm1cxIzVwjoleQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.3.tgz", + "integrity": "sha512-f5dXGzOJycyzSMdaXVhiBhauL4dYydXwVpavfQ1mVCaGjR56a9QfklXObUxlIY9bGTmCPHEEZ04I16BZ/8w5ww==", + "dev": true + }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", "dev": true }, "@types/mocha": { @@ -144,21 +184,11 @@ "dev": true }, "@types/node": { - "version": "9.4.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.4.6.tgz", - "integrity": "sha512-CTUtLb6WqCCgp6P59QintjHWqzf4VL1uPA27bipLAPxFqrtK1gEYllePzTICGqQ8rYsCbpnsNypXjjDzGAAjEQ==", + "version": "9.6.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.12.tgz", + "integrity": "sha512-2Z8ziWjJbvV8hHL5YcqCG9ng+/qwUlO1gB4frBD7QI5Wm1Y1iM+AEkGVEv0S5P+aDMwTtAhPJFR4rp1uqagSig==", "dev": true }, - "JSONStream": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.2.tgz", - "integrity": "sha1-wQI3G27Dp887hHygDCC7D85Mbeo=", - "dev": true, - "requires": { - "jsonparse": "1.3.1", - "through": "2.3.8" - } - }, "abab": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", @@ -166,44 +196,27 @@ "dev": true }, "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", + "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", "dev": true }, - "acorn-globals": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz", - "integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==", + "acorn-dynamic-import": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", + "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", "dev": true, "requires": { "acorn": "5.5.3" - }, - "dependencies": { - "acorn": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", - "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", - "dev": true - } } }, - "acorn-node": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.3.0.tgz", - "integrity": "sha512-efP54n3d1aLfjL2UMdaXa6DsswwzJeI5rqhbFvXMrKiJ6eJFpf+7R0zN7t8IC+XKn2YOAFAv6xbBNgHUkoHWLw==", + "acorn-globals": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz", + "integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==", "dev": true, "requires": { - "acorn": "5.5.3", - "xtend": "4.0.1" - }, - "dependencies": { - "acorn": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", - "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", - "dev": true - } + "acorn": "5.5.3" } }, "ajv": { @@ -263,24 +276,6 @@ "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, - "array-filter": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", - "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", - "dev": true - }, - "array-map": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", - "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", - "dev": true - }, - "array-reduce": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", - "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", - "dev": true - }, "array-unique": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", @@ -309,26 +304,6 @@ "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", "dev": true }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "dev": true, - "requires": { - "util": "0.10.3" - } - }, "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", @@ -341,15 +316,6 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, - "astw": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz", - "integrity": "sha1-e9QXhNMkk5h66yOba04cV6hzuRc=", - "dev": true, - "requires": { - "acorn": "4.0.13" - } - }, "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", @@ -369,9 +335,9 @@ "dev": true }, "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", + "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==", "dev": true }, "babel-code-frame": { @@ -418,12 +384,6 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "base64-js": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.3.tgz", - "integrity": "sha512-MsAhsUW1GxCdgYSO6tAfZrNapmUKk7mWx/k5mFY/A1gBtkaCaNapTg+FExCw1r9yeaZhqx/xPg43xgTFH6KL5w==", - "dev": true - }, "bcrypt-pbkdf": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", @@ -434,12 +394,6 @@ "tweetnacl": "0.14.5" } }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - }, "boom": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", @@ -470,207 +424,28 @@ "repeat-element": "1.1.2" } }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browser-pack": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.4.tgz", - "integrity": "sha512-Q4Rvn7P6ObyWfc4stqLWHtG1MJ8vVtjgT24Zbu+8UTzxYuZouqZsmNRRTFVMY/Ux0eIKv1d+JWzsInTX+fdHPQ==", - "dev": true, - "requires": { - "JSONStream": "1.3.2", - "combine-source-map": "0.8.0", - "defined": "1.0.0", - "safe-buffer": "5.1.1", - "through2": "2.0.3", - "umd": "3.0.1" - } - }, "browser-process-hrtime": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz", "integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44=", "dev": true }, - "browser-resolve": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", - "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=", - "dev": true, - "requires": { - "resolve": "1.1.7" - }, - "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } - } - }, "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, - "browserify": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.1.1.tgz", - "integrity": "sha512-iSH21jK0+IApV8YHOfmGt1qsGd74oflQ1Ko/28JOkWLFNBngAQfKb6WYIJ9CufH8vycqKX1sYU3y7ZrVhwevAg==", - "dev": true, - "requires": { - "JSONStream": "1.3.2", - "assert": "1.4.1", - "browser-pack": "6.0.4", - "browser-resolve": "1.11.2", - "browserify-zlib": "0.2.0", - "buffer": "5.1.0", - "cached-path-relative": "1.0.1", - "concat-stream": "1.6.1", - "console-browserify": "1.1.0", - "constants-browserify": "1.0.0", - "crypto-browserify": "3.12.0", - "defined": "1.0.0", - "deps-sort": "2.0.0", - "domain-browser": "1.2.0", - "duplexer2": "0.1.4", - "events": "2.0.0", - "glob": "7.1.2", - "has": "1.0.1", - "htmlescape": "1.1.1", - "https-browserify": "1.0.0", - "inherits": "2.0.3", - "insert-module-globals": "7.0.2", - "labeled-stream-splicer": "2.0.0", - "mkdirp": "0.5.1", - "module-deps": "6.0.0", - "os-browserify": "0.3.0", - "parents": "1.0.1", - "path-browserify": "0.0.0", - "process": "0.11.10", - "punycode": "1.4.1", - "querystring-es3": "0.2.1", - "read-only-stream": "2.0.0", - "readable-stream": "2.3.5", - "resolve": "1.5.0", - "shasum": "1.0.2", - "shell-quote": "1.6.1", - "stream-browserify": "2.0.1", - "stream-http": "2.8.0", - "string_decoder": "1.0.3", - "subarg": "1.0.0", - "syntax-error": "1.4.0", - "through2": "2.0.3", - "timers-browserify": "1.4.2", - "tty-browserify": "0.0.1", - "url": "0.11.0", - "util": "0.10.3", - "vm-browserify": "0.0.4", - "xtend": "4.0.1" - } - }, - "browserify-aes": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz", - "integrity": "sha512-UGnTYAnB2a3YuYKIRy1/4FB2HdM866E0qC46JXvVTYKlBlZlnvfpSfY6OKfXZAkv70eJ2a1SqzpAo5CRhZGDFg==", - "dev": true, - "requires": { - "buffer-xor": "1.0.3", - "cipher-base": "1.0.4", - "create-hash": "1.1.3", - "evp_bytestokey": "1.0.3", - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "browserify-cipher": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", - "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", - "dev": true, - "requires": { - "browserify-aes": "1.1.1", - "browserify-des": "1.0.0", - "evp_bytestokey": "1.0.3" - } - }, - "browserify-des": { + "buffer-from": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", - "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "des.js": "1.0.0", - "inherits": "2.0.3" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "randombytes": "2.0.6" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "elliptic": "6.4.0", - "inherits": "2.0.3", - "parse-asn1": "5.1.0" - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "1.0.6" - } - }, - "buffer": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.1.0.tgz", - "integrity": "sha512-YkIRgwsZwJWTnyQrsBTWefizHh+8GYj3kbL1BTiAQ/9pwpino0G7B2gp5tx/FUBqUlvtxV85KNR3mwfAtv15Yw==", - "dev": true, - "requires": { - "base64-js": "1.2.3", - "ieee754": "1.1.8" - } - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", + "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", "dev": true }, "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-2.0.0.tgz", + "integrity": "sha512-3U5kUA5VPsRUA3nofm/BXX7GVHKfxz0hOBAPxXrIvHzlDRkQVqEn6yi8QJegxl4LzOHLdvb7XF5dVawa/VVYBg==", "dev": true }, "bytebuffer": { @@ -682,12 +457,6 @@ "long": "3.2.0" } }, - "cached-path-relative": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz", - "integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc=", - "dev": true - }, "camelcase": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", @@ -715,14 +484,14 @@ } }, "chalk": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", - "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", - "supports-color": "5.3.0" + "supports-color": "5.4.0" }, "dependencies": { "has-flag": { @@ -732,9 +501,9 @@ "dev": true }, "supports-color": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz", - "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", + "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" @@ -748,16 +517,6 @@ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "dev": true }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", @@ -769,35 +528,6 @@ "wrap-ansi": "2.1.0" } }, - "clone": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", - "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", - "dev": true - }, - "clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", - "dev": true - }, - "clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", - "dev": true - }, - "cloneable-readable": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.1.tgz", - "integrity": "sha512-DNNEq6JdqBFPzS29TaoqZFPNLn5Xn3XyPFqLIhyBT8Xou4lHQEWzD6FinXoJUfhIfWX3aE1JkRa3cbWCHFbt1g==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "process-nextick-args": "2.0.0", - "readable-stream": "2.3.5" - } - }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -831,18 +561,6 @@ "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g=", "dev": true }, - "combine-source-map": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", - "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", - "dev": true, - "requires": { - "convert-source-map": "1.1.3", - "inline-source-map": "0.6.2", - "lodash.memoize": "3.0.4", - "source-map": "0.5.7" - } - }, "combined-stream": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", @@ -864,42 +582,10 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "concat-stream": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.1.tgz", - "integrity": "sha512-gslSSJx03QKa59cIKqeJO9HQ/WZMotvYJCuaUULrLpjj8oG40kV2Z+gz82pVxlTkOADi4PJxQPPfhl1ELYrrXw==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.5", - "typedarray": "0.0.6" - } - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "0.1.4" - } - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "content-type-parser": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/content-type-parser/-/content-type-parser-1.0.2.tgz", - "integrity": "sha512-lM4l4CnMEwOLHAHr/P6MEZwZFPJFtAAKgL6pogbXmVZggIqXhdB6RbBtPOTsw2FcXwYhehRGERJmRrjOiIB8pQ==", - "dev": true - }, - "convert-source-map": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", - "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", + "core-js": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.5.tgz", + "integrity": "sha1-sU3ek2xkDAV5prUMq8wTLdYSfjs=", "dev": true }, "core-util-is": { @@ -909,52 +595,16 @@ "dev": true }, "coveralls": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.0.tgz", - "integrity": "sha512-ZppXR9y5PraUOrf/DzHJY6gzNUhXYE3b9D43xEXs4QYZ7/Oe0Gy0CS+IPKWFfvQFXB3RG9QduaQUFehzSpGAFw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.1.tgz", + "integrity": "sha512-FAzXwiDOYLGDWH+zgoIA+8GbWv50hlx+kpEJyvzLKOdnIBv9uWoVl4DhqGgyUHpiRjAlF8KYZSipWXYtllWH6Q==", "dev": true, "requires": { "js-yaml": "3.11.0", "lcov-parse": "0.0.10", "log-driver": "1.2.7", "minimist": "1.2.0", - "request": "2.83.0" - } - }, - "create-ecdh": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", - "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "elliptic": "6.4.0" - } - }, - "create-hash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", - "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "inherits": "2.0.3", - "ripemd160": "2.0.1", - "sha.js": "2.4.10" - } - }, - "create-hmac": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", - "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "create-hash": "1.1.3", - "inherits": "2.0.3", - "ripemd160": "2.0.1", - "safe-buffer": "5.1.1", - "sha.js": "2.4.10" + "request": "2.85.0" } }, "cryptiles": { @@ -977,25 +627,6 @@ } } }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "1.0.0", - "browserify-sign": "4.0.4", - "create-ecdh": "4.0.0", - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "diffie-hellman": "5.0.2", - "inherits": "2.0.3", - "pbkdf2": "3.0.14", - "public-encrypt": "4.0.0", - "randombytes": "2.0.6", - "randomfill": "1.0.4" - } - }, "cssom": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz", @@ -1020,11 +651,25 @@ "assert-plus": "1.0.0" } }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true + "data-urls": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.0.tgz", + "integrity": "sha512-ai40PPQR0Fn1lD2PPie79CibnlMN2AYiDhwFX/rZHVsxbs5kNJSjegqXIprhouGXlRdEnfybva7kqRGnB6mypA==", + "dev": true, + "requires": { + "abab": "1.0.4", + "whatwg-mimetype": "2.1.0", + "whatwg-url": "6.4.1" + } + }, + "date-time": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/date-time/-/date-time-2.1.0.tgz", + "integrity": "sha512-/9+C44X7lot0IeiyfgJmETtRMhBidBYM2QFFIkGa0U1k+hSyY87Nw7PY3eDqpvCBm7I3WCSfPeZskW/YYq6m4g==", + "dev": true, + "requires": { + "time-zone": "1.0.0" + } }, "debug": { "version": "3.1.0", @@ -1056,78 +701,22 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, - "deps-sort": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", - "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=", - "dev": true, - "requires": { - "JSONStream": "1.3.2", - "shasum": "1.0.2", - "subarg": "1.0.0", - "through2": "2.0.3" - } - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "detective": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.1.0.tgz", - "integrity": "sha512-TFHMqfOvxlgrfVzTEkNBSh9SvSNX/HfF4OFI2QFGCyPm02EsyILqnUeb5P6q7JZ3SFNTBL5t2sePRgrN4epUWQ==", - "dev": true, - "requires": { - "acorn-node": "1.3.0", - "defined": "1.0.0", - "minimist": "1.2.0" - } - }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, - "diffie-hellman": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", - "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "miller-rabin": "4.0.1", - "randombytes": "2.0.6" - } - }, "dom-storage": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dom-storage/-/dom-storage-2.0.2.tgz", - "integrity": "sha1-7RfL9oq9EOCu+BgnE+KXxeS1ALA=", - "dev": true - }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/dom-storage/-/dom-storage-2.1.0.tgz", + "integrity": "sha512-g6RpyWXzl0RR6OTElHKBl7nwnK87GUyZMYC7JWsB/IA73vpqK2K6LT39x4VepLxlSsWBFrPVLnsSR5Jyty0+2Q==", "dev": true }, "domexception": { @@ -1139,27 +728,6 @@ "webidl-conversions": "4.0.2" } }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "dev": true, - "requires": { - "readable-stream": "2.3.5" - } - }, - "duplexify": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.4.tgz", - "integrity": "sha512-JzYSLYMhoVVBe8+mbHQ4KgpvHpm0DZpJuL8PY93Vyv1fW7jYJ90LoXa1di/CVbJM+TgMs91rbDapE/RNIfnJsA==", - "dev": true, - "requires": { - "end-of-stream": "1.4.1", - "inherits": "2.0.3", - "readable-stream": "2.3.5", - "stream-shift": "1.0.0" - } - }, "ecc-jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", @@ -1170,28 +738,13 @@ "jsbn": "0.1.1" } }, - "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0", - "hash.js": "1.1.3", - "hmac-drbg": "1.0.1", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0", - "minimalistic-crypto-utils": "1.0.1" - } - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", "dev": true, "requires": { - "once": "1.4.0" + "iconv-lite": "0.4.22" } }, "escape-string-regexp": { @@ -1218,13 +771,6 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true } } }, @@ -1240,28 +786,18 @@ "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", "dev": true }, + "estree-walker": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.3.1.tgz", + "integrity": "sha1-5rGlHPcpJSTnI3wxLl/mZgwc4ao=", + "dev": true + }, "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, - "events": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/events/-/events-2.0.0.tgz", - "integrity": "sha512-r/M5YkNg9zwI8QbSf7tsDWWJvO3PGwZXyG7GpFAxtMASnHL2eblFd7iHiGPtyGKKFPZ59S63NeX10Ws6WqGDcg==", - "dev": true - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "1.3.4", - "safe-buffer": "5.1.1" - } - }, "expand-brackets": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", @@ -1286,15 +822,6 @@ "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", "dev": true }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - }, "extglob": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", @@ -1302,14 +829,6 @@ "dev": true, "requires": { "is-extglob": "1.0.0" - }, - "dependencies": { - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - } } }, "extsprintf": { @@ -1365,28 +884,23 @@ } }, "firebase": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-4.10.1.tgz", - "integrity": "sha512-h3rzfitKYBQAIxvj5r41W3zP9B0y3WcDLk3kHE+fW+6Mxo2oDW9qSqWNVOjV1yqiGR/6tXKxJshQzqrbwpU3VA==", - "dev": true, - "requires": { - "@firebase/app": "0.1.10", - "@firebase/auth": "0.3.4", - "@firebase/database": "0.1.11", - "@firebase/firestore": "0.3.4", - "@firebase/messaging": "0.2.1", - "@firebase/polyfill": "0.1.6", - "@firebase/storage": "0.1.8", - "dom-storage": "2.0.2", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-4.13.1.tgz", + "integrity": "sha512-vi+DbUPRrsUaLxuMLgCuyISTBNQ/TqrEwWzUpMqyABPwE6YhGplNnCDPs+/kRDXxdqWUdwrUEXo1ansBSimoNw==", + "dev": true, + "requires": { + "@firebase/app": "0.2.0", + "@firebase/auth": "0.4.2", + "@firebase/database": "0.2.2", + "@firebase/firestore": "0.4.1", + "@firebase/functions": "0.1.1", + "@firebase/messaging": "0.2.4", + "@firebase/polyfill": "0.3.1", + "@firebase/storage": "0.1.9", + "dom-storage": "2.1.0", "xmlhttprequest": "1.8.0" } }, - "first-chunk-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", - "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=", - "dev": true - }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -1419,18 +933,23 @@ "mime-types": "2.1.18" } }, + "fs-extra": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", + "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "4.0.0", + "universalify": "0.1.1" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": 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-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", @@ -1468,107 +987,15 @@ "requires": { "glob-parent": "2.0.0", "is-glob": "2.0.1" - }, - "dependencies": { - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "2.0.1" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - } } }, "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "3.1.0", - "path-dirname": "1.0.2" - } - }, - "glob-stream": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", - "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "extend": "3.0.1", - "glob": "5.0.15", - "glob-parent": "3.1.0", - "micromatch": "2.3.11", - "ordered-read-streams": "0.3.0", - "through2": "0.6.5", - "to-absolute-glob": "0.1.1", - "unique-stream": "2.2.1" - }, - "dependencies": { - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "through2": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", - "dev": true, - "requires": { - "readable-stream": "1.0.34", - "xtend": "4.0.1" - } - } + "is-glob": "2.0.1" } }, "graceful-fs": { @@ -1584,83 +1011,97 @@ "dev": true }, "grpc": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.9.1.tgz", - "integrity": "sha512-WNW3MWMuAoo63AwIlzFE3T0KzzvNBSvOkg67Hm8WhvHNkXFBlIk1QyJRE3Ocm0O5eIwS7JU8Ssota53QR1zllg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.10.1.tgz", + "integrity": "sha512-xmhA11h2XhqpSVzDAmoQAYdNQ+swILXpKOiRpAEQ2kX55ioxVADc6v7SkS4zQBxm4klhQHgGqpGKvoL6LGx4VQ==", "dev": true, "requires": { - "lodash": "4.17.5", - "nan": "2.9.2", - "node-pre-gyp": "0.6.39", + "lodash": "4.17.10", + "nan": "2.10.0", + "node-pre-gyp": "0.7.0", "protobufjs": "5.0.2" }, "dependencies": { "abbrev": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, "ajv": { - "version": "4.11.8", - "bundled": true, + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "dev": true, "requires": { "co": "4.6.0", - "json-stable-stringify": "1.0.1" + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" } }, "ansi-regex": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true }, "are-we-there-yet": { "version": "1.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "dev": true, "requires": { "delegates": "1.0.0", - "readable-stream": "2.3.3" + "readable-stream": "2.3.5" } }, "asn1": { "version": "0.2.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", "dev": true }, "assert-plus": { - "version": "0.2.0", - "bundled": true, + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true }, "asynckit": { "version": "0.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, "aws-sign2": { - "version": "0.6.0", - "bundled": true, + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", "dev": true }, "aws4": { "version": "1.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", "dev": true }, "balanced-match": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, "bcrypt-pbkdf": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", "dev": true, "optional": true, "requires": { @@ -1669,23 +1110,26 @@ }, "block-stream": { "version": "0.0.9", - "bundled": true, + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", "dev": true, "requires": { "inherits": "2.0.3" } }, "boom": { - "version": "2.10.1", - "bundled": true, + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", "dev": true, "requires": { - "hoek": "2.16.3" + "hoek": "4.2.1" } }, "brace-expansion": { "version": "1.1.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", "dev": true, "requires": { "balanced-match": "1.0.0", @@ -1694,22 +1138,26 @@ }, "caseless": { "version": "0.12.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, "co": { "version": "4.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, "code-point-at": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, "combined-stream": { - "version": "1.0.5", - "bundled": true, + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "dev": true, "requires": { "delayed-stream": "1.0.0" @@ -1717,45 +1165,55 @@ }, "concat-map": { "version": "0.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true }, "core-util-is": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, "cryptiles": { - "version": "2.0.5", - "bundled": true, + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", "dev": true, "requires": { - "boom": "2.10.1" + "boom": "5.2.0" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "dev": true, + "requires": { + "hoek": "4.2.1" + } + } } }, "dashdash": { "version": "1.14.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, "requires": { "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true - } } }, "debug": { "version": "2.6.9", - "bundled": true, + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -1763,27 +1221,32 @@ }, "deep-extend": { "version": "0.4.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", "dev": true }, "delayed-stream": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, "delegates": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true }, "detect-libc": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true }, "ecc-jsbn": { "version": "0.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", "dev": true, "optional": true, "requires": { @@ -1792,37 +1255,55 @@ }, "extend": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", "dev": true }, "extsprintf": { "version": "1.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", "dev": true }, "forever-agent": { "version": "0.6.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", "dev": true }, "form-data": { - "version": "2.1.4", - "bundled": true, + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "dev": true, "requires": { "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" + "combined-stream": "1.0.6", + "mime-types": "2.1.18" } }, "fs.realpath": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, "fstream": { "version": "1.0.11", - "bundled": true, + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", "dev": true, "requires": { "graceful-fs": "4.1.11", @@ -1833,7 +1314,8 @@ }, "fstream-ignore": { "version": "1.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", "dev": true, "requires": { "fstream": "1.0.11", @@ -1843,7 +1325,8 @@ }, "gauge": { "version": "2.7.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "requires": { "aproba": "1.2.0", @@ -1858,22 +1341,17 @@ }, "getpass": { "version": "0.1.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, "requires": { "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true - } } }, "glob": { "version": "7.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { "fs.realpath": "1.0.0", @@ -1886,57 +1364,65 @@ }, "graceful-fs": { "version": "4.1.11", - "bundled": true, + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, "har-schema": { - "version": "1.0.5", - "bundled": true, + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", "dev": true }, "har-validator": { - "version": "4.2.1", - "bundled": true, + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "dev": true, "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" + "ajv": "5.5.2", + "har-schema": "2.0.0" } }, "has-unicode": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true }, "hawk": { - "version": "3.1.3", - "bundled": true, + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", "dev": true, "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.1", + "sntp": "2.1.0" } }, "hoek": { - "version": "2.16.3", - "bundled": true, + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", + "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", "dev": true }, "http-signature": { - "version": "1.1.1", - "bundled": true, + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, "requires": { - "assert-plus": "0.2.0", + "assert-plus": "1.0.0", "jsprim": "1.4.1", - "sshpk": "1.13.1" + "sshpk": "1.14.1" } }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { "once": "1.4.0", @@ -1945,17 +1431,20 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, "ini": { "version": "1.3.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true, + "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.1" @@ -1963,117 +1452,124 @@ }, "is-typedarray": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, "isarray": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, "isstream": { "version": "0.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, "jsbn": { "version": "0.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true, "optional": true }, "json-schema": { "version": "0.2.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", "dev": true }, - "json-stable-stringify": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "jsonify": "0.0.0" - } + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true }, "json-stringify-safe": { "version": "5.0.1", - "bundled": true, - "dev": true - }, - "jsonify": { - "version": "0.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, "jsprim": { "version": "1.4.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", "json-schema": "0.2.3", "verror": "1.10.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true - } } }, "mime-db": { - "version": "1.30.0", - "bundled": true, + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", "dev": true }, "mime-types": { - "version": "2.1.17", - "bundled": true, + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "dev": true, "requires": { - "mime-db": "1.30.0" + "mime-db": "1.33.0" } }, "minimatch": { "version": "3.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "1.1.8" } }, "minimist": { - "version": "0.0.8", - "bundled": true, + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, "mkdirp": { "version": "0.5.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } } }, "ms": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, "node-pre-gyp": { - "version": "0.6.39", - "bundled": true, + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.7.0.tgz", + "integrity": "sha1-Va7/uu2TtQ0KRlfUaRmM2ArJ3zY=", "dev": true, "requires": { "detect-libc": "1.0.3", - "hawk": "3.1.3", "mkdirp": "0.5.1", "nopt": "4.0.1", "npmlog": "4.1.2", - "rc": "1.2.4", - "request": "2.81.0", + "rc": "1.2.6", + "request": "2.83.0", "rimraf": "2.6.2", "semver": "5.5.0", "tar": "2.2.1", @@ -2082,16 +1578,18 @@ }, "nopt": { "version": "4.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "requires": { "abbrev": "1.1.1", - "osenv": "0.1.4" + "osenv": "0.1.5" } }, "npmlog": { "version": "4.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "requires": { "are-we-there-yet": "1.1.4", @@ -2102,22 +1600,26 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, "oauth-sign": { "version": "0.8.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", "dev": true }, "object-assign": { "version": "4.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1.0.2" @@ -2125,17 +1627,20 @@ }, "os-homedir": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, "os-tmpdir": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, "osenv": { - "version": "0.1.4", - "bundled": true, + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "requires": { "os-homedir": "1.0.2", @@ -2144,93 +1649,95 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "performance-now": { - "version": "0.2.0", - "bundled": true, + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, "process-nextick-args": { - "version": "1.0.7", - "bundled": true, + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, "punycode": { "version": "1.4.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "dev": true }, "qs": { - "version": "6.4.0", - "bundled": true, + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", "dev": true }, "rc": { - "version": "1.2.4", - "bundled": true, + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.6.tgz", + "integrity": "sha1-6xiYnG1PTxYsOZ953dKfODVWgJI=", "dev": true, "requires": { "deep-extend": "0.4.2", "ini": "1.3.5", "minimist": "1.2.0", "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true - } } }, "readable-stream": { - "version": "2.3.3", - "bundled": true, + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz", + "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", "dev": true, "requires": { "core-util-is": "1.0.2", "inherits": "2.0.3", "isarray": "1.0.0", - "process-nextick-args": "1.0.7", + "process-nextick-args": "2.0.0", "safe-buffer": "5.1.1", "string_decoder": "1.0.3", "util-deprecate": "1.0.2" } }, "request": { - "version": "2.81.0", - "bundled": true, + "version": "2.83.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", + "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", "dev": true, "requires": { - "aws-sign2": "0.6.0", + "aws-sign2": "0.7.0", "aws4": "1.6.0", "caseless": "0.12.0", - "combined-stream": "1.0.5", + "combined-stream": "1.0.6", "extend": "3.0.1", "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", + "form-data": "2.3.2", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", "is-typedarray": "1.0.0", "isstream": "0.1.2", "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", + "mime-types": "2.1.18", "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", + "performance-now": "2.1.0", + "qs": "6.5.1", "safe-buffer": "5.1.1", "stringstream": "0.0.5", - "tough-cookie": "2.3.3", + "tough-cookie": "2.3.4", "tunnel-agent": "0.6.0", "uuid": "3.2.1" } }, "rimraf": { "version": "2.6.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { "glob": "7.1.2" @@ -2238,35 +1745,41 @@ }, "safe-buffer": { "version": "5.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", "dev": true }, "semver": { "version": "5.5.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "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", - "bundled": true, + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, "sntp": { - "version": "1.0.9", - "bundled": true, + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", "dev": true, "requires": { - "hoek": "2.16.3" + "hoek": "4.2.1" } }, "sshpk": { - "version": "1.13.1", - "bundled": true, + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", + "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", "dev": true, "requires": { "asn1": "0.2.3", @@ -2277,18 +1790,12 @@ "getpass": "0.1.7", "jsbn": "0.1.1", "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true - } } }, "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { "code-point-at": "1.1.0", @@ -2298,7 +1805,8 @@ }, "string_decoder": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { "safe-buffer": "5.1.1" @@ -2306,12 +1814,14 @@ }, "stringstream": { "version": "0.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", "dev": true }, "strip-ansi": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { "ansi-regex": "2.1.1" @@ -2319,12 +1829,14 @@ }, "strip-json-comments": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, "tar": { "version": "2.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "dev": true, "requires": { "block-stream": "0.0.9", @@ -2334,22 +1846,24 @@ }, "tar-pack": { "version": "3.4.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.1.tgz", + "integrity": "sha512-PPRybI9+jM5tjtCbN2cxmmRU7YmqT3Zv/UDy48tAh2XRkLa9bAORtSWLkVc13+GJF+cdTh1yEnHEk3cpTaL5Kg==", "dev": true, "requires": { "debug": "2.6.9", "fstream": "1.0.11", "fstream-ignore": "1.0.5", "once": "1.4.0", - "readable-stream": "2.3.3", + "readable-stream": "2.3.5", "rimraf": "2.6.2", "tar": "2.2.1", "uid-number": "0.0.6" } }, "tough-cookie": { - "version": "2.3.3", - "bundled": true, + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", "dev": true, "requires": { "punycode": "1.4.1" @@ -2357,7 +1871,8 @@ }, "tunnel-agent": { "version": "0.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, "requires": { "safe-buffer": "5.1.1" @@ -2365,45 +1880,44 @@ }, "tweetnacl": { "version": "0.14.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "dev": true, "optional": true }, "uid-number": { "version": "0.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", "dev": true }, "util-deprecate": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, "uuid": { "version": "3.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", "dev": true }, "verror": { "version": "1.10.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, "requires": { "assert-plus": "1.0.0", "core-util-is": "1.0.2", "extsprintf": "1.3.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true - } } }, "wide-align": { "version": "1.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "dev": true, "requires": { "string-width": "1.0.2" @@ -2411,52 +1925,9 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, - "dev": true - } - } - }, - "gulp-sourcemaps": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz", - "integrity": "sha1-uG/zSdgBzrVuHZ59x7vLS33uYAw=", - "dev": true, - "requires": { - "convert-source-map": "1.1.3", - "graceful-fs": "4.1.11", - "strip-bom": "2.0.0", - "through2": "2.0.3", - "vinyl": "1.2.0" - }, - "dependencies": { - "clone": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", - "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=", - "dev": true - }, - "clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", - "dev": true - }, - "replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true - }, - "vinyl": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", - "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", - "dev": true, - "requires": { - "clone": "1.0.3", - "clone-stats": "0.0.1", - "replace-ext": "0.0.1" - } } } }, @@ -2476,15 +1947,6 @@ "har-schema": "2.0.0" } }, - "has": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", - "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", - "dev": true, - "requires": { - "function-bind": "1.1.1" - } - }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -2500,25 +1962,6 @@ "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", "dev": true }, - "hash-base": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", - "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, "hawk": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", @@ -2537,17 +1980,6 @@ "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "1.1.3", - "minimalistic-assert": "1.0.0", - "minimalistic-crypto-utils": "1.0.1" - } - }, "hoek": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", @@ -2563,16 +1995,10 @@ "whatwg-encoding": "1.0.3" } }, - "htmlescape": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", - "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", - "dev": true - }, "http-parser-js": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.11.tgz", - "integrity": "sha512-QCR5O2AjjMW8Mo4HyI1ctFcv+O99j/0g367V3YoVnrNw5hkDvAWZD0lWGcc+F4yN3V55USPCVix4efb75HxFfA==", + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.12.tgz", + "integrity": "sha1-uc+/Sizybw/DSxDKFImid3HjR08=", "dev": true }, "http-signature": { @@ -2583,32 +2009,17 @@ "requires": { "assert-plus": "1.0.0", "jsprim": "1.4.1", - "sshpk": "1.13.1" + "sshpk": "1.14.1" } }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", - "dev": true - }, - "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", - "dev": true - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true + "version": "0.4.22", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.22.tgz", + "integrity": "sha512-1AinFBeDTnsvVEP+V1QBlHpM1UZZl7gWB6fcz7B1Ho+LI1dUh2sSrxoCfVt2PinRHzXAziSniEV3P7JbTDHcXA==", + "dev": true, + "requires": { + "safer-buffer": "2.1.2" + } }, "inflight": { "version": "1.0.6", @@ -2626,88 +2037,18 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, - "inline-source-map": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", - "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", - "dev": true, - "requires": { - "source-map": "0.5.7" - } - }, - "insert-module-globals": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.2.tgz", - "integrity": "sha512-p3s7g96Nm62MbHRuj9ZXab0DuJNWD7qcmdUXCOQ/ZZn42DtDXfsLill7bq19lDCx3K3StypqUnuE3H2VmIJFUw==", - "dev": true, - "requires": { - "JSONStream": "1.3.2", - "combine-source-map": "0.7.2", - "concat-stream": "1.5.2", - "is-buffer": "1.1.6", - "lexical-scope": "1.2.0", - "process": "0.11.10", - "through2": "2.0.3", - "xtend": "4.0.1" - }, - "dependencies": { - "combine-source-map": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.7.2.tgz", - "integrity": "sha1-CHAxKFazB6h8xKxIbzqaYq7MwJ4=", - "dev": true, - "requires": { - "convert-source-map": "1.1.3", - "inline-source-map": "0.6.2", - "lodash.memoize": "3.0.4", - "source-map": "0.5.7" - } - }, - "concat-stream": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", - "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.0.6", - "typedarray": "0.0.6" - } - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "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=", "dev": true }, + "irregular-plurals": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-1.4.0.tgz", + "integrity": "sha1-LKmwM2UREYVUEvFr5dd8YqRYp2Y=", + "dev": true + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -2736,9 +2077,9 @@ "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=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", "dev": true }, "is-fullwidth-code-point": { @@ -2751,14 +2092,20 @@ } }, "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "1.0.0" } }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "dev": true + }, "is-number": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", @@ -2780,6 +2127,23 @@ "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", "dev": true }, + "is-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.0.tgz", + "integrity": "sha512-h37O/IX4efe56o9k41II1ECMqKwtqHa7/12dLDEzJIFux2x15an4WCDb0/eKdmUgRpLJ3bR0DrzDc7vOrVgRDw==", + "dev": true, + "requires": { + "@types/estree": "0.0.38" + }, + "dependencies": { + "@types/estree": { + "version": "0.0.38", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.38.tgz", + "integrity": "sha512-F/v7t1LwS4vnXuPooJQGBRKRGIoxWUTmA4VHfqjOccFsNDThD5bfUNpITive6s352O7o384wcpEaDV8rHCehDA==", + "dev": true + } + } + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -2792,18 +2156,6 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-valid-glob": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", - "integrity": "sha1-1LVcafUYhvm2XHDWwmItN+KfSP4=", - "dev": true - }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -2819,6 +2171,16 @@ "isarray": "1.0.0" } }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "dev": true, + "requires": { + "node-fetch": "1.7.3", + "whatwg-fetch": "2.0.4" + } + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -2849,27 +2211,26 @@ "optional": true }, "jsdom": { - "version": "11.6.2", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.6.2.tgz", - "integrity": "sha512-pAeZhpbSlUp5yQcS6cBQJwkbzmv4tWFaYxHbFVSxzXefqjvtRA851Z5N2P+TguVG9YeUDcgb8pdeVQRJh0XR3Q==", + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.10.0.tgz", + "integrity": "sha512-x5No5FpJgBg3j5aBwA8ka6eGuS5IxbC8FOkmyccKvObtFT0bDMict/LOxINZsZGZSfGdNomLZ/qRV9Bpq/GIBA==", "dev": true, "requires": { "abab": "1.0.4", "acorn": "5.5.3", "acorn-globals": "4.1.0", "array-equal": "1.0.0", - "browser-process-hrtime": "0.1.2", - "content-type-parser": "1.0.2", "cssom": "0.3.2", "cssstyle": "0.2.37", + "data-urls": "1.0.0", "domexception": "1.0.1", "escodegen": "1.9.1", "html-encoding-sniffer": "1.0.2", - "left-pad": "1.2.0", - "nwmatcher": "1.4.3", + "left-pad": "1.3.0", + "nwmatcher": "1.4.4", "parse5": "4.0.0", "pn": "1.1.0", - "request": "2.83.0", + "request": "2.85.0", "request-promise-native": "1.0.5", "sax": "1.2.4", "symbol-tree": "3.2.2", @@ -2877,17 +2238,10 @@ "w3c-hr-time": "1.0.1", "webidl-conversions": "4.0.2", "whatwg-encoding": "1.0.3", - "whatwg-url": "6.4.0", + "whatwg-mimetype": "2.1.0", + "whatwg-url": "6.4.1", "ws": "4.1.0", "xml-name-validator": "3.0.0" - }, - "dependencies": { - "acorn": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", - "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", - "dev": true - } } }, "jsdom-global": { @@ -2908,32 +2262,20 @@ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", "dev": true }, - "json-stable-stringify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", - "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=", - "dev": true, - "requires": { - "jsonify": "0.0.0" - } - }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "dev": true + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11" + } }, "jsprim": { "version": "1.4.1", @@ -2956,34 +2298,6 @@ "is-buffer": "1.1.6" } }, - "labeled-stream-splicer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz", - "integrity": "sha1-pS4dE4AkwAuGscDJH2d5GLiuClk=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "isarray": "0.0.1", - "stream-splicer": "2.0.0" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - } - } - }, - "lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", - "dev": true, - "requires": { - "readable-stream": "2.3.5" - } - }, "lcid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", @@ -2999,21 +2313,10 @@ "integrity": "sha1-GwuP+ayceIklBYK3C3ExXZ2m2aM=", "dev": true }, - "lcov-result-merger": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcov-result-merger/-/lcov-result-merger-2.0.0.tgz", - "integrity": "sha512-CMjYOcPtl0G3SLrPWYDtZ4zyhsy/2heUf6RKwvyDcJRfyUBQbqLhAlS2PBhTKVCxGp7Ri8AYJ5SGNVEtbEo0fA==", - "dev": true, - "requires": { - "through2": "2.0.3", - "vinyl": "2.1.0", - "vinyl-fs": "2.4.4" - } - }, "left-pad": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.2.0.tgz", - "integrity": "sha1-0wpzxrggHY99jnlWupYWCHpo4O4=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", "dev": true }, "levn": { @@ -3026,31 +2329,16 @@ "type-check": "0.3.2" } }, - "lexical-scope": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.2.0.tgz", - "integrity": "sha1-/Ope3HBKSzqHls3KQZw6CvryLfQ=", - "dev": true, - "requires": { - "astw": "2.2.0" - } - }, - "lodash": { - "version": "4.17.5", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", - "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", - "dev": true - }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", + "locate-character": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-2.0.5.tgz", + "integrity": "sha512-n2GmejDXtOPBAZdIiEFy5dJ5N38xBCXLNOtw2WpB9kGh6pnrEuKlwYI+Tkpofc4wDtVXHtoAOJaMRlYG/oYaxg==", "dev": true }, - "lodash.memoize": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", - "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", "dev": true }, "lodash.sortby": { @@ -3071,43 +2359,21 @@ "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", "dev": true }, + "magic-string": { + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", + "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==", + "dev": true, + "requires": { + "vlq": "0.2.3" + } + }, "make-error": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.4.tgz", "integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==", "dev": true }, - "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", - "dev": true, - "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.3" - }, - "dependencies": { - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - } - } - }, - "merge-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", - "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", - "dev": true, - "requires": { - "readable-stream": "2.3.5" - } - }, "micromatch": { "version": "2.3.11", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", @@ -3127,33 +2393,6 @@ "object.omit": "2.0.1", "parse-glob": "3.0.4", "regex-cache": "0.4.4" - }, - "dependencies": { - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - } - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0" } }, "mime-db": { @@ -3171,18 +2410,6 @@ "mime-db": "1.33.0" } }, - "minimalistic-assert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", - "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -3216,9 +2443,9 @@ } }, "mocha": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.4.tgz", - "integrity": "sha512-nMOpAPFosU1B4Ix1jdhx5e3q7XO55ic5a8cgYvW27CequcEY+BabS0kUVL1Cw1V5PuVHZWeNRWFLmEPexo79VA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.1.1.tgz", + "integrity": "sha512-kKKs/H1KrMMQIEsWNxGmb4/BGsmj0dkeyotEvbrAuQ01FcWRLssUNXCEUZk6SZtyJBi6EE7SL0zDDtItw1rGhw==", "dev": true, "requires": { "browser-stdout": "1.3.1", @@ -3229,33 +2456,11 @@ "glob": "7.1.2", "growl": "1.10.3", "he": "1.1.1", + "minimatch": "3.0.4", "mkdirp": "0.5.1", "supports-color": "4.4.0" } }, - "module-deps": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.0.0.tgz", - "integrity": "sha512-BKsMhJJENEM4dTgqq2MDTTHXRHcNUFegoAwlG4HO4VMdUyMcJDKgfgI+MOv6tR5Iv8G3MKZFgsSiyP3ZoosRMw==", - "dev": true, - "requires": { - "JSONStream": "1.3.2", - "browser-resolve": "1.11.2", - "cached-path-relative": "1.0.1", - "concat-stream": "1.6.1", - "defined": "1.0.0", - "detective": "5.1.0", - "duplexer2": "0.1.4", - "inherits": "2.0.3", - "parents": "1.0.1", - "readable-stream": "2.3.5", - "resolve": "1.5.0", - "stream-combiner2": "1.1.1", - "subarg": "1.0.0", - "through2": "2.0.3", - "xtend": "4.0.1" - } - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -3263,11 +2468,21 @@ "dev": true }, "nan": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.9.2.tgz", - "integrity": "sha512-ltW65co7f3PQWBDbqVvaU1WtFJUsNW7sWWm4HINhbMQIyVyzIeyZ8toX5TC5eeooE6piZoaEh4cZkueSKG3KYw==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", "dev": true }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "dev": true, + "requires": { + "encoding": "0.1.12", + "is-stream": "1.1.0" + } + }, "normalize-path": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", @@ -3284,15 +2499,15 @@ "dev": true }, "nwmatcher": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.3.tgz", - "integrity": "sha512-IKdSTiDWCarf2JTS5e9e2+5tPZGdkRJ79XjYV0pzK8Q9BpsFyBq1RGKxzs7Q8UBushGw7m6TzVKz6fcY99iSWw==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.4.tgz", + "integrity": "sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ==", "dev": true }, "nyc": { - "version": "11.4.1", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-11.4.1.tgz", - "integrity": "sha512-5eCZpvaksFVjP2rt1r60cfXmt3MUtsQDw8bAzNqNEr4WLvUMLgiVENMf/B9bE9YAX0mGVvaGA3v9IS9ekNqB1Q==", + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-11.7.1.tgz", + "integrity": "sha512-EGePURSKUEpS1jWnEKAMhY+GWZzi7JC+f8iBDOATaOsLZW5hM/9eYx2dHGaEXa1ITvMm44CJugMksvP3NwMQMw==", "dev": true, "requires": { "archy": "1.0.0", @@ -3305,28 +2520,29 @@ "find-up": "2.1.0", "foreground-child": "1.5.6", "glob": "7.1.2", - "istanbul-lib-coverage": "1.1.1", + "istanbul-lib-coverage": "1.2.0", "istanbul-lib-hook": "1.1.0", - "istanbul-lib-instrument": "1.9.1", - "istanbul-lib-report": "1.1.2", - "istanbul-lib-source-maps": "1.2.2", - "istanbul-reports": "1.1.3", + "istanbul-lib-instrument": "1.10.1", + "istanbul-lib-report": "1.1.3", + "istanbul-lib-source-maps": "1.2.3", + "istanbul-reports": "1.4.0", "md5-hex": "1.3.0", - "merge-source-map": "1.0.4", + "merge-source-map": "1.1.0", "micromatch": "2.3.11", "mkdirp": "0.5.1", "resolve-from": "2.0.0", "rimraf": "2.6.2", "signal-exit": "3.0.2", "spawn-wrap": "1.4.2", - "test-exclude": "4.1.1", - "yargs": "10.0.3", - "yargs-parser": "8.0.0" + "test-exclude": "4.2.1", + "yargs": "11.1.0", + "yargs-parser": "8.1.0" }, "dependencies": { "align-text": { "version": "0.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { "kind-of": "3.2.2", @@ -3336,22 +2552,26 @@ }, "amdefine": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, "ansi-regex": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "ansi-styles": { "version": "2.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, "append-transform": { "version": "0.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", + "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", "dev": true, "requires": { "default-require-extensions": "1.0.0" @@ -3359,12 +2579,14 @@ }, "archy": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, "arr-diff": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { "arr-flatten": "1.1.0" @@ -3372,27 +2594,50 @@ }, "arr-flatten": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, "array-unique": { "version": "0.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", "dev": true }, "arrify": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, "async": { "version": "1.5.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "atob": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.0.tgz", + "integrity": "sha512-SuiKH8vbsOyCALjA/+EINmt/Kdl+TQPrtFgW7XZZcwtryFu9e5kQoX3bjCW6mIvGH1fbeAZZuvwGR5IlBRznGw==", "dev": true }, "babel-code-frame": { "version": "6.26.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { "chalk": "1.1.3", @@ -3401,8 +2646,9 @@ } }, "babel-generator": { - "version": "6.26.0", - "bundled": true, + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", "dev": true, "requires": { "babel-messages": "6.23.0", @@ -3410,14 +2656,15 @@ "babel-types": "6.26.0", "detect-indent": "4.0.0", "jsesc": "1.3.0", - "lodash": "4.17.4", + "lodash": "4.17.5", "source-map": "0.5.7", "trim-right": "1.0.1" } }, "babel-messages": { "version": "6.23.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { "babel-runtime": "6.26.0" @@ -3425,28 +2672,31 @@ }, "babel-runtime": { "version": "6.26.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.3", + "core-js": "2.5.5", "regenerator-runtime": "0.11.1" } }, "babel-template": { "version": "6.26.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, "requires": { "babel-runtime": "6.26.0", "babel-traverse": "6.26.0", "babel-types": "6.26.0", "babylon": "6.18.0", - "lodash": "4.17.4" + "lodash": "4.17.5" } }, "babel-traverse": { "version": "6.26.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, "requires": { "babel-code-frame": "6.26.0", @@ -3456,34 +2706,105 @@ "babylon": "6.18.0", "debug": "2.6.9", "globals": "9.18.0", - "invariant": "2.2.2", - "lodash": "4.17.4" + "invariant": "2.2.4", + "lodash": "4.17.5" } }, "babel-types": { "version": "6.26.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { "babel-runtime": "6.26.0", "esutils": "2.0.2", - "lodash": "4.17.4", + "lodash": "4.17.5", "to-fast-properties": "1.0.3" } }, "babylon": { "version": "6.18.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true }, "balanced-match": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.2.1", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.1", + "pascalcase": "0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, "brace-expansion": { - "version": "1.1.8", - "bundled": true, + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "1.0.0", @@ -3492,7 +2813,8 @@ }, "braces": { "version": "1.8.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { "expand-range": "1.8.2", @@ -3502,12 +2824,39 @@ }, "builtin-modules": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "1.0.0", + "component-emitter": "1.2.1", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.0", + "to-object-path": "0.3.0", + "union-value": "1.0.0", + "unset-value": "1.0.0" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, "caching-transform": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-1.0.1.tgz", + "integrity": "sha1-bb2y8g+Nj7znnz6U6dF0Lc31wKE=", "dev": true, "requires": { "md5-hex": "1.3.0", @@ -3517,13 +2866,15 @@ }, "camelcase": { "version": "1.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", "dev": true, "optional": true }, "center-align": { "version": "0.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "dev": true, "optional": true, "requires": { @@ -3533,7 +2884,8 @@ }, "chalk": { "version": "1.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { "ansi-styles": "2.2.1", @@ -3543,9 +2895,39 @@ "supports-color": "2.0.0" } }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, "cliui": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "dev": true, "optional": true, "requires": { @@ -3556,7 +2938,8 @@ "dependencies": { "wordwrap": { "version": "0.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", "dev": true, "optional": true } @@ -3564,41 +2947,70 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, - "commondir": { - "version": "1.0.1", - "bundled": true, - "dev": true + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "1.0.0", + "object-visit": "1.0.1" + } + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true }, "concat-map": { "version": "0.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "convert-source-map": { "version": "1.5.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", + "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", + "dev": true + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, "core-js": { - "version": "2.5.3", - "bundled": true, + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.5.tgz", + "integrity": "sha1-sU3ek2xkDAV5prUMq8wTLdYSfjs=", "dev": true }, "cross-spawn": { "version": "4.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", "dev": true, "requires": { - "lru-cache": "4.1.1", + "lru-cache": "4.1.2", "which": "1.3.0" } }, "debug": { "version": "2.6.9", - "bundled": true, + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -3606,25 +3018,88 @@ }, "debug-log": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", + "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", "dev": true }, "decamelize": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, "default-require-extensions": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", + "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", "dev": true, "requires": { "strip-bom": "2.0.0" } }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "1.0.2", + "isobject": "3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, "detect-indent": { "version": "4.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true, "requires": { "repeating": "2.0.1" @@ -3632,7 +3107,8 @@ }, "error-ex": { "version": "1.3.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", "dev": true, "requires": { "is-arrayish": "0.2.1" @@ -3640,17 +3116,20 @@ }, "escape-string-regexp": { "version": "1.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, "esutils": { "version": "2.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, "execa": { "version": "0.7.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { "cross-spawn": "5.1.0", @@ -3664,10 +3143,11 @@ "dependencies": { "cross-spawn": { "version": "5.1.0", - "bundled": true, + "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.1.1", + "lru-cache": "4.1.2", "shebang-command": "1.2.0", "which": "1.3.0" } @@ -3676,7 +3156,8 @@ }, "expand-brackets": { "version": "0.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { "is-posix-bracket": "0.1.1" @@ -3684,15 +3165,38 @@ }, "expand-range": { "version": "1.8.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { "fill-range": "2.2.3" } }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, "extglob": { "version": "0.3.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { "is-extglob": "1.0.0" @@ -3700,12 +3204,14 @@ }, "filename-regex": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", "dev": true }, "fill-range": { "version": "2.2.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", "dev": true, "requires": { "is-number": "2.1.0", @@ -3717,7 +3223,8 @@ }, "find-cache-dir": { "version": "0.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", + "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", "dev": true, "requires": { "commondir": "1.0.1", @@ -3727,7 +3234,8 @@ }, "find-up": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { "locate-path": "2.0.0" @@ -3735,12 +3243,14 @@ }, "for-in": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, "for-own": { "version": "0.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "dev": true, "requires": { "for-in": "1.0.2" @@ -3748,31 +3258,51 @@ }, "foreground-child": { "version": "1.5.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", + "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", "dev": true, "requires": { "cross-spawn": "4.0.2", "signal-exit": "3.0.2" } }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "0.2.2" + } + }, "fs.realpath": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, "get-caller-file": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", "dev": true }, "get-stream": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, "glob": { "version": "7.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { "fs.realpath": "1.0.0", @@ -3785,7 +3315,8 @@ }, "glob-base": { "version": "0.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "dev": true, "requires": { "glob-parent": "2.0.0", @@ -3794,7 +3325,8 @@ }, "glob-parent": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { "is-glob": "2.0.1" @@ -3802,17 +3334,20 @@ }, "globals": { "version": "9.18.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", "dev": true }, "graceful-fs": { "version": "4.1.11", - "bundled": true, + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, "handlebars": { "version": "4.0.11", - "bundled": true, + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", + "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", "dev": true, "requires": { "async": "1.5.2", @@ -3823,7 +3358,8 @@ "dependencies": { "source-map": { "version": "0.4.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { "amdefine": "1.0.1" @@ -3833,7 +3369,8 @@ }, "has-ansi": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { "ansi-regex": "2.1.1" @@ -3841,22 +3378,86 @@ }, "has-flag": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", "dev": true }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, "hosted-git-info": { - "version": "2.5.0", - "bundled": true, + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", + "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==", "dev": true }, "imurmurhash": { "version": "0.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { "once": "1.4.0", @@ -3865,12 +3466,14 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, "invariant": { - "version": "2.2.2", - "bundled": true, + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "dev": true, "requires": { "loose-envify": "1.3.1" @@ -3878,35 +3481,78 @@ }, "invert-kv": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", "dev": true }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, "is-arrayish": { "version": "0.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, "is-buffer": { "version": "1.1.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, "is-builtin-module": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { "builtin-modules": "1.1.1" } }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, "is-dotfile": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", "dev": true }, "is-equal-shallow": { "version": "0.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "dev": true, "requires": { "is-primitive": "2.0.0" @@ -3914,33 +3560,35 @@ }, "is-extendable": { "version": "0.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, "is-extglob": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", "dev": true }, "is-finite": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { "number-is-nan": "1.0.1" } }, "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true }, "is-glob": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { "is-extglob": "1.0.0" @@ -3948,83 +3596,135 @@ }, "is-number": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "dev": true, "requires": { "kind-of": "3.2.2" } }, + "is-odd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", + "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", + "dev": true, + "requires": { + "is-number": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, "is-posix-bracket": { "version": "0.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", "dev": true }, "is-primitive": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", "dev": true }, "is-stream": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, "is-utf8": { "version": "0.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, "isarray": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, "isexe": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, "isobject": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", "dev": true, "requires": { "isarray": "1.0.0" } }, "istanbul-lib-coverage": { - "version": "1.1.1", - "bundled": true, + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz", + "integrity": "sha512-GvgM/uXRwm+gLlvkWHTjDAvwynZkL9ns15calTrmhGgowlwJBbWMYzWbKqE2DT6JDP1AFXKa+Zi0EkqNCUqY0A==", "dev": true }, "istanbul-lib-hook": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz", + "integrity": "sha512-U3qEgwVDUerZ0bt8cfl3dSP3S6opBoOtk3ROO5f2EfBr/SRiD9FQqzwaZBqFORu8W7O0EXpai+k7kxHK13beRg==", "dev": true, "requires": { "append-transform": "0.4.0" } }, "istanbul-lib-instrument": { - "version": "1.9.1", - "bundled": true, + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz", + "integrity": "sha512-1dYuzkOCbuR5GRJqySuZdsmsNKPL3PTuyPevQfoCXJePT9C8y1ga75neU+Tuy9+yS3G/dgx8wgOmp2KLpgdoeQ==", "dev": true, "requires": { - "babel-generator": "6.26.0", + "babel-generator": "6.26.1", "babel-template": "6.26.0", "babel-traverse": "6.26.0", "babel-types": "6.26.0", "babylon": "6.18.0", - "istanbul-lib-coverage": "1.1.1", - "semver": "5.4.1" + "istanbul-lib-coverage": "1.2.0", + "semver": "5.5.0" } }, "istanbul-lib-report": { - "version": "1.1.2", - "bundled": true, + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.3.tgz", + "integrity": "sha512-D4jVbMDtT2dPmloPJS/rmeP626N5Pr3Rp+SovrPn1+zPChGHcggd/0sL29jnbm4oK9W0wHjCRsdch9oLd7cm6g==", "dev": true, "requires": { - "istanbul-lib-coverage": "1.1.1", + "istanbul-lib-coverage": "1.2.0", "mkdirp": "0.5.1", "path-parse": "1.0.5", "supports-color": "3.2.3" @@ -4032,7 +3732,8 @@ "dependencies": { "supports-color": { "version": "3.2.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { "has-flag": "1.0.0" @@ -4041,12 +3742,13 @@ } }, "istanbul-lib-source-maps": { - "version": "1.2.2", - "bundled": true, + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.3.tgz", + "integrity": "sha512-fDa0hwU/5sDXwAklXgAoCJCOsFsBplVQ6WBldz5UwaqOzmDhUK4nfuR7/G//G2lERlblUNJB8P6e8cXq3a7MlA==", "dev": true, "requires": { "debug": "3.1.0", - "istanbul-lib-coverage": "1.1.1", + "istanbul-lib-coverage": "1.2.0", "mkdirp": "0.5.1", "rimraf": "2.6.2", "source-map": "0.5.7" @@ -4054,7 +3756,8 @@ "dependencies": { "debug": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -4063,8 +3766,9 @@ } }, "istanbul-reports": { - "version": "1.1.3", - "bundled": true, + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.4.0.tgz", + "integrity": "sha512-OPzVo1fPZ2H+owr8q/LYKLD+vquv9Pj4F+dj808MdHbuQLD7S4ACRjcX+0Tne5Vxt2lxXvdZaL7v+FOOAV281w==", "dev": true, "requires": { "handlebars": "4.0.11" @@ -4072,17 +3776,20 @@ }, "js-tokens": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, "jsesc": { "version": "1.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", "dev": true }, "kind-of": { "version": "3.2.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { "is-buffer": "1.1.6" @@ -4090,13 +3797,15 @@ }, "lazy-cache": { "version": "1.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", "dev": true, "optional": true }, "lcid": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, "requires": { "invert-kv": "1.0.0" @@ -4104,7 +3813,8 @@ }, "load-json-file": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { "graceful-fs": "4.1.11", @@ -4116,7 +3826,8 @@ }, "locate-path": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { "p-locate": "2.0.0", @@ -4125,41 +3836,62 @@ "dependencies": { "path-exists": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true } } }, "lodash": { - "version": "4.17.4", - "bundled": true, + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", "dev": true }, "longest": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", "dev": true }, "loose-envify": { "version": "1.3.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "dev": true, "requires": { "js-tokens": "3.0.2" } }, "lru-cache": { - "version": "4.1.1", - "bundled": true, + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz", + "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==", "dev": true, "requires": { "pseudomap": "1.0.2", "yallist": "2.1.2" } }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "1.0.1" + } + }, "md5-hex": { "version": "1.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-1.3.0.tgz", + "integrity": "sha1-0sSv6YPENwZiF5uMrRRSGRNQRsQ=", "dev": true, "requires": { "md5-o-matic": "0.1.1" @@ -4167,28 +3899,40 @@ }, "md5-o-matic": { "version": "0.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/md5-o-matic/-/md5-o-matic-0.1.1.tgz", + "integrity": "sha1-givM1l4RfFFPqxdrJZRdVBAKA8M=", "dev": true }, "mem": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { - "mimic-fn": "1.1.0" + "mimic-fn": "1.2.0" } }, "merge-source-map": { - "version": "1.0.4", - "bundled": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", "dev": true, "requires": { - "source-map": "0.5.7" + "source-map": "0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "micromatch": { "version": "2.3.11", - "bundled": true, + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { "arr-diff": "2.0.0", @@ -4207,26 +3951,51 @@ } }, "mimic-fn": { - "version": "1.1.0", - "bundled": true, + "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", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.8" + "brace-expansion": "1.1.11" } }, "minimist": { "version": "0.0.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "1.0.2", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, "mkdirp": { "version": "0.5.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { "minimist": "0.0.8" @@ -4234,23 +4003,66 @@ }, "ms": { "version": "2.0.0", - "bundled": true, - "dev": true + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nanomatch": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", + "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "fragment-cache": "0.2.1", + "is-odd": "2.0.0", + "is-windows": "1.0.2", + "kind-of": "6.0.2", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } }, "normalize-package-data": { "version": "2.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { - "hosted-git-info": "2.5.0", + "hosted-git-info": "2.6.0", "is-builtin-module": "1.0.0", - "semver": "5.4.1", - "validate-npm-package-license": "3.0.1" + "semver": "5.5.0", + "validate-npm-package-license": "3.0.3" } }, "normalize-path": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { "remove-trailing-separator": "1.1.0" @@ -4258,7 +4070,8 @@ }, "npm-run-path": { "version": "2.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { "path-key": "2.0.1" @@ -4266,26 +4079,86 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, "object-assign": { "version": "4.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, "object.omit": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true, "requires": { "for-own": "0.1.5", "is-extendable": "0.1.1" } }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1.0.2" @@ -4293,7 +4166,8 @@ }, "optimist": { "version": "0.6.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { "minimist": "0.0.8", @@ -4302,12 +4176,14 @@ }, "os-homedir": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, "os-locale": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "requires": { "execa": "0.7.0", @@ -4317,25 +4193,38 @@ }, "p-finally": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, "p-limit": { - "version": "1.1.0", - "bundled": true, - "dev": true + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", + "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", + "dev": true, + "requires": { + "p-try": "1.0.0" + } }, "p-locate": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "1.1.0" + "p-limit": "1.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=", + "dev": true + }, "parse-glob": { "version": "3.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "dev": true, "requires": { "glob-base": "0.3.0", @@ -4346,15 +4235,23 @@ }, "parse-json": { "version": "2.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { "error-ex": "1.3.1" } }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, "path-exists": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { "pinkie-promise": "2.0.1" @@ -4362,22 +4259,26 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "path-key": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, "path-parse": { "version": "1.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", "dev": true }, "path-type": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { "graceful-fs": "4.1.11", @@ -4387,17 +4288,20 @@ }, "pify": { "version": "2.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, "pinkie": { "version": "2.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", "dev": true }, "pinkie-promise": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { "pinkie": "2.0.4" @@ -4405,7 +4309,8 @@ }, "pkg-dir": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", "dev": true, "requires": { "find-up": "1.1.2" @@ -4413,7 +4318,8 @@ "dependencies": { "find-up": { "version": "1.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { "path-exists": "2.1.0", @@ -4422,19 +4328,28 @@ } } }, - "preserve": { + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "preserve": { "version": "0.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", "dev": true }, "pseudomap": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, "randomatic": { "version": "1.1.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", "dev": true, "requires": { "is-number": "3.0.0", @@ -4443,7 +4358,8 @@ "dependencies": { "is-number": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { "kind-of": "3.2.2" @@ -4451,7 +4367,8 @@ "dependencies": { "kind-of": { "version": "3.2.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { "is-buffer": "1.1.6" @@ -4461,7 +4378,8 @@ }, "kind-of": { "version": "4.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { "is-buffer": "1.1.6" @@ -4471,7 +4389,8 @@ }, "read-pkg": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { "load-json-file": "1.1.0", @@ -4481,7 +4400,8 @@ }, "read-pkg-up": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { "find-up": "1.1.2", @@ -4490,7 +4410,8 @@ "dependencies": { "find-up": { "version": "1.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { "path-exists": "2.1.0", @@ -4501,35 +4422,51 @@ }, "regenerator-runtime": { "version": "0.11.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", "dev": true }, "regex-cache": { "version": "0.4.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "requires": { "is-equal-shallow": "0.1.3" } }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" + } + }, "remove-trailing-separator": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", "dev": true }, "repeat-element": { "version": "1.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", "dev": true }, "repeat-string": { "version": "1.6.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, "repeating": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { "is-finite": "1.0.2" @@ -4537,22 +4474,38 @@ }, "require-directory": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, "require-main-filename": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", "dev": true }, "resolve-from": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, "right-align": { "version": "0.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "dev": true, "optional": true, "requires": { @@ -4561,25 +4514,61 @@ }, "rimraf": { "version": "2.6.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { "glob": "7.1.2" } }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "0.1.15" + } + }, "semver": { - "version": "5.4.1", - "bundled": true, + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, "shebang-command": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { "shebang-regex": "1.0.0" @@ -4587,27 +4576,159 @@ }, "shebang-regex": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, "slide": { "version": "1.1.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", "dev": true }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.1", + "use": "3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, "source-map": { "version": "0.5.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", + "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", + "dev": true, + "requires": { + "atob": "2.1.0", + "decode-uri-component": "0.2.0", + "resolve-url": "0.2.1", + "source-map-url": "0.4.0", + "urix": "0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, "spawn-wrap": { "version": "1.4.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.2.tgz", + "integrity": "sha512-vMwR3OmmDhnxCVxM8M+xO/FtIp6Ju/mNaDfCMMW7FDcLRTPFWUswec4LXJHTJE2hwTI9O0YBfygu4DalFl7Ylg==", "dev": true, "requires": { "foreground-child": "1.5.6", @@ -4619,26 +4740,71 @@ } }, "spdx-correct": { - "version": "1.0.2", - "bundled": true, + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", + "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", "dev": true, "requires": { - "spdx-license-ids": "1.2.2" + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.0" } }, - "spdx-expression-parse": { - "version": "1.0.4", - "bundled": true, + "spdx-exceptions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", "dev": true }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "2.1.0", + "spdx-license-ids": "3.0.0" + } + }, "spdx-license-ids": { - "version": "1.2.2", - "bundled": true, + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", + "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", "dev": true }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "3.0.2" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "0.2.5", + "object-copy": "0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + } + } + }, "string-width": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { "is-fullwidth-code-point": "2.0.0", @@ -4647,17 +4813,14 @@ "dependencies": { "ansi-regex": { "version": "3.0.0", - "bundled": true, - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "strip-ansi": { "version": "4.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { "ansi-regex": "3.0.0" @@ -4667,7 +4830,8 @@ }, "strip-ansi": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { "ansi-regex": "2.1.1" @@ -4675,7 +4839,8 @@ }, "strip-bom": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { "is-utf8": "0.2.1" @@ -4683,39 +4848,363 @@ }, "strip-eof": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, "supports-color": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, "test-exclude": { - "version": "4.1.1", - "bundled": true, + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.2.1.tgz", + "integrity": "sha512-qpqlP/8Zl+sosLxBcVKl9vYy26T9NPalxSzzCP/OY6K7j938ui2oKgo+kRZYfxAeIpLqpbVnsHq1tyV70E4lWQ==", "dev": true, "requires": { "arrify": "1.0.1", - "micromatch": "2.3.11", + "micromatch": "3.1.10", "object-assign": "4.1.1", "read-pkg-up": "1.0.1", "require-main-filename": "1.0.1" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.2", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.9", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + } + } } }, "to-fast-properties": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", "dev": true }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "3.0.0", + "repeat-string": "1.6.1" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + } + } + }, "trim-right": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, "uglify-js": { "version": "2.8.29", - "bundled": true, + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "dev": true, "optional": true, "requires": { @@ -4726,7 +5215,8 @@ "dependencies": { "yargs": { "version": "3.10.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true, "optional": true, "requires": { @@ -4740,22 +5230,129 @@ }, "uglify-to-browserify": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "dev": true, "optional": true }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" + } + } + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "0.3.1", + "isobject": "3.0.1" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "use": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", + "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, "validate-npm-package-license": { - "version": "3.0.1", - "bundled": true, + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", + "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", "dev": true, "requires": { - "spdx-correct": "1.0.2", - "spdx-expression-parse": "1.0.4" + "spdx-correct": "3.0.0", + "spdx-expression-parse": "3.0.0" } }, "which": { "version": "1.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "dev": true, "requires": { "isexe": "2.0.0" @@ -4763,32 +5360,46 @@ }, "which-module": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, "window-size": { "version": "0.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", "dev": true, "optional": true }, "wordwrap": { "version": "0.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", "dev": true }, "wrap-ansi": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { "string-width": "1.0.2", "strip-ansi": "3.0.1" }, "dependencies": { + "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.1" + } + }, "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { "code-point-at": "1.1.0", @@ -4800,12 +5411,14 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, "write-file-atomic": { "version": "1.3.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", + "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", "dev": true, "requires": { "graceful-fs": "4.1.11", @@ -4815,20 +5428,23 @@ }, "y18n": { "version": "3.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", "dev": true }, "yallist": { "version": "2.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true }, "yargs": { - "version": "10.0.3", - "bundled": true, + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", + "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", "dev": true, "requires": { - "cliui": "3.2.0", + "cliui": "4.0.0", "decamelize": "1.2.0", "find-up": "2.1.0", "get-caller-file": "1.0.2", @@ -4839,36 +5455,56 @@ "string-width": "2.1.1", "which-module": "2.0.0", "y18n": "3.2.1", - "yargs-parser": "8.0.0" + "yargs-parser": "9.0.2" }, "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, "cliui": { - "version": "3.2.0", - "bundled": true, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.0.0.tgz", + "integrity": "sha512-nY3W5Gu2racvdDk//ELReY+dHjb9PlIcVDFXP72nVIhq2Gy3LuVXYwJoPVudwQnv1shtohpgkdCKT2YaKY0CKw==", "dev": true, "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", + "string-width": "2.1.1", + "strip-ansi": "4.0.0", "wrap-ansi": "2.1.0" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - } + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + }, + "yargs-parser": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", + "dev": true, + "requires": { + "camelcase": "4.1.0" } } } }, "yargs-parser": { - "version": "8.0.0", - "bundled": true, + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", + "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", "dev": true, "requires": { "camelcase": "4.1.0" @@ -4876,7 +5512,8 @@ "dependencies": { "camelcase": { "version": "4.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "dev": true } } @@ -4889,12 +5526,6 @@ "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", "dev": true }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, "object.omit": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", @@ -4934,22 +5565,6 @@ "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4=", "dev": true }, - "ordered-read-streams": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", - "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", - "dev": true, - "requires": { - "is-stream": "1.1.0", - "readable-stream": "2.3.5" - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, "os-locale": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", @@ -4959,34 +5574,6 @@ "lcid": "1.0.0" } }, - "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", - "dev": true - }, - "parents": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", - "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", - "dev": true, - "requires": { - "path-platform": "0.11.15" - } - }, - "parse-asn1": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", - "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", - "dev": true, - "requires": { - "asn1.js": "4.10.1", - "browserify-aes": "1.1.1", - "create-hash": "1.1.3", - "evp_bytestokey": "1.0.3", - "pbkdf2": "3.0.14" - } - }, "parse-glob": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", @@ -4997,43 +5584,20 @@ "is-dotfile": "1.0.3", "is-extglob": "1.0.0", "is-glob": "2.0.1" - }, - "dependencies": { - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - } } }, + "parse-ms": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz", + "integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0=", + "dev": true + }, "parse5": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", "dev": true }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -5046,37 +5610,27 @@ "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", "dev": true }, - "path-platform": { - "version": "0.11.15", - "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", - "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", - "dev": true - }, "pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", "dev": true }, - "pbkdf2": { - "version": "3.0.14", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", - "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==", - "dev": true, - "requires": { - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "ripemd160": "2.0.1", - "safe-buffer": "5.1.1", - "sha.js": "2.4.10" - } - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "plur": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/plur/-/plur-2.1.2.tgz", + "integrity": "sha1-dIJFLBoPUI4+NE6uwxLJHCncZVo=", + "dev": true, + "requires": { + "irregular-plurals": "1.4.0" + } + }, "pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", @@ -5095,22 +5649,20 @@ "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", "dev": true }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true + "pretty-ms": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-3.1.0.tgz", + "integrity": "sha1-6crJx2v27lL+lC3ZxsQhMVOxKIE=", + "dev": true, + "requires": { + "parse-ms": "1.0.1", + "plur": "2.1.2" + } }, "promise-polyfill": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-7.1.0.tgz", - "integrity": "sha512-P6NJ2wU/8fac44ENORsuqT8TiolKGB2u0fEClPtXezn7w5cmLIjM/7mhPlTebke2EPr6tmqZbXvnX0TxwykGrg==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-7.1.2.tgz", + "integrity": "sha512-FuEc12/eKqqoRYIGBrUptCBRhobL19PS2U31vMNTfyck1FxPyMfgsXyW4Mav85y/ZN1hop3hOwRlUDok23oYfQ==", "dev": true }, "protobufjs": { @@ -5125,19 +5677,6 @@ "yargs": "3.32.0" } }, - "public-encrypt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", - "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.1.3", - "parse-asn1": "5.1.0", - "randombytes": "2.0.6" - } - }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -5145,21 +5684,9 @@ "dev": true }, "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", - "dev": true - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, "randomatic": { @@ -5196,54 +5723,11 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "randombytes": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", - "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "2.0.6", - "safe-buffer": "5.1.1" - } - }, - "read-only-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", - "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", - "dev": true, - "requires": { - "readable-stream": "2.3.5" - } - }, - "readable-stream": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz", - "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } } }, "regex-cache": { @@ -5273,20 +5757,14 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", - "dev": true - }, "request": { - "version": "2.83.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", - "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", + "version": "2.85.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz", + "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==", "dev": true, "requires": { "aws-sign2": "0.7.0", - "aws4": "1.6.0", + "aws4": "1.7.0", "caseless": "0.12.0", "combined-stream": "1.0.6", "extend": "3.0.1", @@ -5301,8 +5779,8 @@ "mime-types": "2.1.18", "oauth-sign": "0.8.2", "performance-now": "2.1.0", - "qs": "6.5.1", - "safe-buffer": "5.1.1", + "qs": "6.5.2", + "safe-buffer": "5.1.2", "stringstream": "0.0.5", "tough-cookie": "2.3.4", "tunnel-agent": "0.6.0", @@ -5315,7 +5793,7 @@ "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", "dev": true, "requires": { - "lodash": "4.17.5" + "lodash": "4.17.10" } }, "request-promise-native": { @@ -5329,29 +5807,130 @@ "tough-cookie": "2.3.4" } }, + "require-relative": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", + "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", + "dev": true + }, "resolve": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", - "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", + "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", "dev": true, "requires": { "path-parse": "1.0.5" } }, - "ripemd160": { + "rollup": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.57.1.tgz", + "integrity": "sha512-I18GBqP0qJoJC1K1osYjreqA8VAKovxuI3I81RSk0Dmr4TgloI0tAULjZaox8OsJ+n7XRrhH6i0G2By/pj1LCA==", + "dev": true, + "requires": { + "@types/acorn": "4.0.3", + "acorn": "5.5.3", + "acorn-dynamic-import": "3.0.0", + "date-time": "2.1.0", + "is-reference": "1.1.0", + "locate-character": "2.0.5", + "pretty-ms": "3.1.0", + "require-relative": "0.8.7", + "rollup-pluginutils": "2.0.1", + "signal-exit": "3.0.2", + "sourcemap-codec": "1.4.1" + } + }, + "rollup-plugin-commonjs": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.1.0.tgz", + "integrity": "sha512-NrfE0g30QljNCnlJr7I2Xguz+44mh0dCxvfxwLnCwtaCK2LwFUp1zzAs8MQuOfhH4mRskqsjfOwGUap/L+WtEw==", + "dev": true, + "requires": { + "estree-walker": "0.5.2", + "magic-string": "0.22.5", + "resolve": "1.7.1", + "rollup-pluginutils": "2.0.1" + }, + "dependencies": { + "estree-walker": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.5.2.tgz", + "integrity": "sha512-XpCnW/AE10ws/kDAs37cngSkvgIR8aN3G0MS85m7dUpuK2EREo9VJ00uvw6Dg/hXEpfsE1I1TvJOJr+Z+TL+ig==", + "dev": true + } + } + }, + "rollup-plugin-node-resolve": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.3.0.tgz", + "integrity": "sha512-9zHGr3oUJq6G+X0oRMYlzid9fXicBdiydhwGChdyeNRGPcN/majtegApRKHLR5drboUvEWU+QeUmGTyEZQs3WA==", + "dev": true, + "requires": { + "builtin-modules": "2.0.0", + "is-module": "1.0.0", + "resolve": "1.7.1" + } + }, + "rollup-plugin-typescript2": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.12.0.tgz", + "integrity": "sha512-UvHzfJyBWyV+d4Av7FG79PTipIK1VrNQoDrNWszAYd7i/ZcCfBg3rPkPiK9Z+WEHXeWBbCnxJlSqR2zKWgEkiw==", + "dev": true, + "requires": { + "fs-extra": "5.0.0", + "resolve": "1.7.1", + "rollup-pluginutils": "2.0.1", + "tslib": "1.9.0" + } + }, + "rollup-plugin-uglify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-uglify/-/rollup-plugin-uglify-3.0.0.tgz", + "integrity": "sha512-dehLu9eRRoV4l09aC+ySntRw1OAfoyKdbk8Nelblj03tHoynkSybqyEpgavemi1LBOH6S1vzI58/mpxkZIe1iQ==", + "dev": true, + "requires": { + "uglify-es": "3.3.9" + }, + "dependencies": { + "commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "dev": true + }, + "uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "dev": true, + "requires": { + "commander": "2.13.0", + "source-map": "0.6.1" + } + } + } + }, + "rollup-pluginutils": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", - "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.0.1.tgz", + "integrity": "sha1-fslbNXP2VDpGpkYb2afFRFJdD8A=", "dev": true, "requires": { - "hash-base": "2.0.2", - "inherits": "2.0.3" + "estree-walker": "0.3.1", + "micromatch": "2.3.11" } }, "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, "sax": { @@ -5366,37 +5945,11 @@ "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true }, - "sha.js": { - "version": "2.4.10", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.10.tgz", - "integrity": "sha512-vnwmrFDlOExK4Nm16J2KMWHLrp14lBrjxMxBJpu++EnsuBmpiYaM/MEs46Vxxm/4FvdP5yTwuCTO9it5FSjrqA==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "shasum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz", - "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=", - "dev": true, - "requires": { - "json-stable-stringify": "0.0.1", - "sha.js": "2.4.10" - } - }, - "shell-quote": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", - "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", - "dev": true, - "requires": { - "array-filter": "0.0.1", - "array-map": "0.0.0", - "array-reduce": "0.0.0", - "jsonify": "0.0.0" - } + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true }, "sntp": { "version": "2.1.0", @@ -5408,28 +5961,27 @@ } }, "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "source-map-support": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.3.tgz", - "integrity": "sha512-eKkTgWYeBOQqFGXRfKabMFdnWepo51vWqEdoeikaEPFiJC7MCU5j2h4+6Q8npkZTeLGbSyecZvRxiSoWl3rh+w==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.5.tgz", + "integrity": "sha512-mR7/Nd5l1z6g99010shcXJiNEaf3fEtmLhRB/sBcQVJGodcHCULPp2y4Sfa43Kv2zq7T+Izmfp/WHCR6dYkQCA==", "dev": true, "requires": { + "buffer-from": "1.0.0", "source-map": "0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, + "sourcemap-codec": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.1.tgz", + "integrity": "sha512-hX1eNBNuilj8yfFnECh0DzLgwKpBLMIvmhgEhixXNui8lMLBInTI8Kyxt++RwJnMNu7cAUo635L2+N1TxMJCzA==", + "dev": true + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -5437,9 +5989,9 @@ "dev": true }, "sshpk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", + "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", "dev": true, "requires": { "asn1": "0.2.3", @@ -5458,55 +6010,6 @@ "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", "dev": true }, - "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.5" - } - }, - "stream-combiner2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", - "dev": true, - "requires": { - "duplexer2": "0.1.4", - "readable-stream": "2.3.5" - } - }, - "stream-http": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.0.tgz", - "integrity": "sha512-sZOFxI/5xw058XIRHl4dU3dZ+TTOIGJR78Dvo0oEAejIt4ou27k+3ne1zYmCV+v7UucbxIFQuOgnkTVHh8YPnw==", - "dev": true, - "requires": { - "builtin-status-codes": "3.0.0", - "inherits": "2.0.3", - "readable-stream": "2.3.5", - "to-arraybuffer": "1.0.1", - "xtend": "4.0.1" - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true - }, - "stream-splicer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz", - "integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.5" - } - }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -5518,15 +6021,6 @@ "strip-ansi": "3.0.1" } }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, "stringstream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", @@ -5542,34 +6036,6 @@ "ansi-regex": "2.1.1" } }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "0.2.1" - } - }, - "strip-bom-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", - "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", - "dev": true, - "requires": { - "first-chunk-stream": "1.0.0", - "strip-bom": "2.0.0" - } - }, - "subarg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", - "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", - "dev": true, - "requires": { - "minimist": "1.2.0" - } - }, "supports-color": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", @@ -5585,63 +6051,10 @@ "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", "dev": true }, - "syntax-error": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", - "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", - "dev": true, - "requires": { - "acorn-node": "1.3.0" - } - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", - "dev": true, - "requires": { - "readable-stream": "2.3.5", - "xtend": "4.0.1" - } - }, - "through2-filter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", - "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", - "dev": true, - "requires": { - "through2": "2.0.3", - "xtend": "4.0.1" - } - }, - "timers-browserify": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", - "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", - "dev": true, - "requires": { - "process": "0.11.10" - } - }, - "to-absolute-glob": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz", - "integrity": "sha1-HN+kcqnvUMI57maZm2YsoOs5k38=", - "dev": true, - "requires": { - "extend-shallow": "2.0.1" - } - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "time-zone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", + "integrity": "sha1-mcW/VZWJZq9tBtg73zgA3IL67F0=", "dev": true }, "tough-cookie": { @@ -5677,12 +6090,12 @@ "dev": true, "requires": { "arrify": "1.0.1", - "chalk": "2.3.2", + "chalk": "2.4.1", "diff": "3.5.0", "make-error": "1.3.4", "minimist": "1.2.0", "mkdirp": "0.5.1", - "source-map-support": "0.5.3", + "source-map-support": "0.5.5", "yn": "2.0.0" } }, @@ -5693,55 +6106,55 @@ "dev": true }, "tslint": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.9.1.tgz", - "integrity": "sha1-ElX4ej/1frCw4fDmEKi0dIBGya4=", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.10.0.tgz", + "integrity": "sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM=", "dev": true, "requires": { "babel-code-frame": "6.26.0", "builtin-modules": "1.1.1", - "chalk": "2.3.2", - "commander": "2.15.0", + "chalk": "2.4.1", + "commander": "2.15.1", "diff": "3.5.0", "glob": "7.1.2", "js-yaml": "3.11.0", "minimatch": "3.0.4", - "resolve": "1.5.0", + "resolve": "1.7.1", "semver": "5.5.0", "tslib": "1.9.0", - "tsutils": "2.22.2" + "tsutils": "2.26.2" }, "dependencies": { + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, "commander": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.0.tgz", - "integrity": "sha512-7B1ilBwtYSbetCgTY1NJFg+gVpestg0fdA1MhC1Vs4ssyfSXnCAjFr+QcQM9/RedXC0EaUx1sG8Smgw2VfgKEg==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true } } }, "tsutils": { - "version": "2.22.2", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.22.2.tgz", - "integrity": "sha512-u06FUSulCJ+Y8a2ftuqZN6kIGqdP2yJjUPEngXqmdPND4UQfb04igcotH+dw+IFr417yP6muCLE8/5/Qlfnx0w==", + "version": "2.26.2", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.26.2.tgz", + "integrity": "sha512-uzwnhmrSbyinPCiwfzGsOY3IulBTwoky7r83HmZdz9QNCjhSCzavkh47KLWuU0zF2F2WbpmmzoJUIEiYyd+jEQ==", "dev": true, "requires": { "tslib": "1.9.0" } }, - "tty-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", - "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", - "dev": true - }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "5.1.2" } }, "tweetnacl": { @@ -5766,108 +6179,16 @@ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, "typescript": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz", - "integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.8.3.tgz", + "integrity": "sha512-K7g15Bb6Ra4lKf7Iq2l/I5/En+hLIHmxWZGq3D4DIRNFxMNV6j2SHSvDOqs2tGd4UvD/fJvrwopzQXjLrT7Itw==", "dev": true }, - "uglify-js": { - "version": "3.3.13", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.13.tgz", - "integrity": "sha512-7rdn/bDOG1ElSTPdh7AI5TCjLv63ZD4k8BBadN3ssIkhlaQL2c0yRxmXCyOYhZK0wZTgGgUSnYQ4CGu+Jos5cA==", - "dev": true, - "requires": { - "commander": "2.14.1", - "source-map": "0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz", - "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "umd": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.1.tgz", - "integrity": "sha1-iuVW4RAR9jwllnCKiDclnwGz1g4=", - "dev": true - }, - "unique-stream": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", - "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", - "dev": true, - "requires": { - "json-stable-stringify": "1.0.1", - "through2-filter": "2.0.0" - }, - "dependencies": { - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "requires": { - "jsonify": "0.0.0" - } - } - } - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "universalify": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", + "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", "dev": true }, "uuid": { @@ -5876,12 +6197,6 @@ "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", "dev": true }, - "vali-date": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", - "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=", - "dev": true - }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -5893,84 +6208,11 @@ "extsprintf": "1.3.0" } }, - "vinyl": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz", - "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", - "dev": true, - "requires": { - "clone": "2.1.1", - "clone-buffer": "1.0.0", - "clone-stats": "1.0.0", - "cloneable-readable": "1.1.1", - "remove-trailing-separator": "1.1.0", - "replace-ext": "1.0.0" - } - }, - "vinyl-fs": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz", - "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", - "dev": true, - "requires": { - "duplexify": "3.5.4", - "glob-stream": "5.3.5", - "graceful-fs": "4.1.11", - "gulp-sourcemaps": "1.6.0", - "is-valid-glob": "0.3.0", - "lazystream": "1.0.0", - "lodash.isequal": "4.5.0", - "merge-stream": "1.0.1", - "mkdirp": "0.5.1", - "object-assign": "4.1.1", - "readable-stream": "2.3.5", - "strip-bom": "2.0.0", - "strip-bom-stream": "1.0.0", - "through2": "2.0.3", - "through2-filter": "2.0.0", - "vali-date": "1.0.0", - "vinyl": "1.2.0" - }, - "dependencies": { - "clone": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", - "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=", - "dev": true - }, - "clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", - "dev": true - }, - "replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", - "dev": true - }, - "vinyl": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", - "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", - "dev": true, - "requires": { - "clone": "1.0.3", - "clone-stats": "0.0.1", - "replace-ext": "0.0.1" - } - } - } - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } + "vlq": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", + "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", + "dev": true }, "w3c-hr-time": { "version": "1.0.1", @@ -5993,7 +6235,7 @@ "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", "dev": true, "requires": { - "http-parser-js": "0.4.11", + "http-parser-js": "0.4.12", "websocket-extensions": "0.1.3" } }, @@ -6010,12 +6252,32 @@ "dev": true, "requires": { "iconv-lite": "0.4.19" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "dev": true + } } }, + "whatwg-fetch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", + "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==", + "dev": true + }, + "whatwg-mimetype": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz", + "integrity": "sha512-FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC+m0Mew==", + "dev": true + }, "whatwg-url": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.4.0.tgz", - "integrity": "sha512-Z0CVh/YE217Foyb488eo+iBv+r7eAQ0wSTyApi9n06jhcA3z6Nidg/EGvl0UFkg7kMdKxfBzzr+o9JF+cevgMg==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.4.1.tgz", + "integrity": "sha512-FwygsxsXx27x6XXuExA/ox3Ktwcbf+OAvrKmLulotDAiO1Q6ixchPFaHYsis2zZBZSJTR0+dR+JVtf7MlbqZjw==", "dev": true, "requires": { "lodash.sortby": "4.7.0", @@ -6058,7 +6320,7 @@ "dev": true, "requires": { "async-limiter": "1.0.0", - "safe-buffer": "5.1.1" + "safe-buffer": "5.1.2" } }, "xml-name-validator": { @@ -6073,12 +6335,6 @@ "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=", "dev": true }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, "y18n": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", diff --git a/package.json b/package.json index 13e60822..544c5810 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,10 @@ "realtime", "geolocation" ], - "main": "dist/index.js", + "main": "dist/index.node.cjs.js", + "browser": "dist/index.cjs.js", + "module": "dist/index.esm.js", + "typings": "dist/index.d.ts", "files": [ "dist/**", "LICENSE", @@ -33,7 +36,6 @@ "@types/chai": "^4.1.2", "@types/mocha": "^2.2.48", "@types/node": "^9.4.6", - "browserify": "^16.1.1", "chai": "^4.1.2", "coveralls": "^3.0.0", "firebase": "4.x.x", @@ -41,19 +43,21 @@ "jsdom-global": "^3.0.2", "mocha": "^5.0.4", "nyc": "^11.4.1", + "rollup": "0.57.1", + "rollup-plugin-commonjs": "9.1.0", + "rollup-plugin-node-resolve": "3.3.0", + "rollup-plugin-typescript2": "0.12.0", + "rollup-plugin-uglify": "3.0.0", "source-map-support": "^0.5.3", "ts-node": "^5.0.1", "tslint": "^5.9.1", - "typescript": "^2.7.2", - "uglify-js": "^3.3.13" + "typescript": "^2.7.2" }, "scripts": { - "browserify": "browserify dist/geoFire/index.js -o dist/browser/geofire.js", - "build": "rm -rf ./dist && tsc && npm run browserify && npm run uglify", + "build": "rollup -c", "coverage": "nyc report --reporter=text-lcov | coveralls", "test": "nyc --reporter=html --reporter=text mocha", - "travis": "npm run test && npm run build", - "uglify": "uglifyjs dist/browser/geofire.js -c -m -o dist/browser/geofire.min.js" + "prepare": "npm run build" }, "nyc": { "extension": [ diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 00000000..58c27940 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,48 @@ +import commonjs from 'rollup-plugin-commonjs'; +import resolveModule from 'rollup-plugin-node-resolve'; +import typescript from 'rollup-plugin-typescript2'; +import uglify from 'rollup-plugin-uglify'; +import pkg from './package.json'; + +const GLOBAL_NAME = 'geofire'; + +const plugins = [ + resolveModule(), + typescript({ + typescript: require('typescript') + }), + commonjs() +]; + +const external = Object.keys(pkg.dependencies || {}); + +const completeBuilds = [{ + input: 'src/index.ts', + output: [{ + file: pkg.browser, + format: 'cjs' + }, + { + file: pkg.main, + format: 'cjs' + }, + { + file: pkg.module, + format: 'es' + } + ], + plugins, + external + }, + { + input: 'src/index.ts', + output: { + file: 'dist/geofire.js', + format: 'umd', + name: GLOBAL_NAME + }, + plugins: [...plugins, uglify()] + }, +]; + +export default [...completeBuilds]; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index e09fdafd..d4748d96 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,4 @@ +export { GeoCallbackRegistration } from './geoFire/geoCallbackRegistration'; export { GeoFire } from './geoFire'; +export { GeoQuery } from './geoFire/geoQuery'; export * from './geoFire/interfaces'; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index e64181e0..c8a33765 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,22 +1,22 @@ { + "compileOnSave": false, "compilerOptions": { - "outDir": "./dist", - "baseUrl": ".", - "inlineSourceMap": true, "declaration": true, + "importHelpers": true, + "lib": [ + "es2015", + "dom" + ], "moduleResolution": "node", - "emitDecoratorMetadata": true, - "experimentalDecorators": true, + "noImplicitAny": false, + "outDir": "dist", + "sourceMap": false, "target": "es5", "typeRoots": [ "node_modules/@types" - ], - "lib": [ - "es2016", - "dom" ] }, - "files": [ - "./src/index.ts" + "include": [ + "src" ] } \ No newline at end of file From 69ebd9bed5bd5e93926d6f3321a106a5fcfa44c0 Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Thu, 10 May 2018 10:48:39 -0700 Subject: [PATCH 18/23] refactor(geofire): break out distance function --- src/geoFire/geoFireUtils.ts | 28 +++++++++++++++++++++++++++- src/geoFire/geoQuery.ts | 9 ++++----- src/geoFire/index.ts | 19 +++---------------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/geoFire/geoFireUtils.ts b/src/geoFire/geoFireUtils.ts index 55cd1c13..0376a83c 100644 --- a/src/geoFire/geoFireUtils.ts +++ b/src/geoFire/geoFireUtils.ts @@ -448,4 +448,30 @@ export function geoFirestoreGetKey(snapshot: firebase.firestore.DocumentSnapshot id = snapshot.id; } return id; -} \ No newline at end of file +} + + /** + * Method which calculates the distance, in kilometers, between two locations, + * via the Haversine formula. Note that this is approximate due to the fact that the + * Earth's radius varies between 6356.752 km and 6378.137 km. + * + * @param location1 The [latitude, longitude] pair of the first location. + * @param location2 The [latitude, longitude] pair of the second location. + * @returns The distance, in kilometers, between the inputted locations. + */ + export function distance(location1: number[], location2: number[]): number { + validateLocation(location1); + validateLocation(location2); + + var radius = 6371; // Earth's radius in kilometers + var latDelta = degreesToRadians(location2[0] - location1[0]); + var lonDelta = degreesToRadians(location2[1] - location1[1]); + + var a = (Math.sin(latDelta / 2) * Math.sin(latDelta / 2)) + + (Math.cos(degreesToRadians(location1[0])) * Math.cos(degreesToRadians(location2[0])) * + Math.sin(lonDelta / 2) * Math.sin(lonDelta / 2)); + + var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + + return radius * c; + }; \ No newline at end of file diff --git a/src/geoFire/geoQuery.ts b/src/geoFire/geoQuery.ts index e65fec77..4935280b 100644 --- a/src/geoFire/geoQuery.ts +++ b/src/geoFire/geoQuery.ts @@ -1,8 +1,7 @@ import * as firebase from 'firebase'; -import { GeoFire } from './index'; import { GeoCallbackRegistration } from './geoCallbackRegistration'; -import { decodeGeoFireObject, encodeGeohash, geoFireGetKey, geohashQueries, validateCriteria, validateLocation } from './geoFireUtils'; +import { decodeGeoFireObject, distance, encodeGeohash, geoFireGetKey, geohashQueries, validateCriteria, validateLocation } from './geoFireUtils'; import { QueryCriteria } from './interfaces'; @@ -192,7 +191,7 @@ export class GeoQuery { // Save if the location was already in the query const wasAlreadyInQuery = locationDict.isInQuery; // Update the location's distance to the new query center - locationDict.distanceFromCenter = GeoFire.distance(locationDict.location, this._center); + locationDict.distanceFromCenter = distance(locationDict.location, this._center); // Determine if the location is now in this query locationDict.isInQuery = (locationDict.distanceFromCenter <= this._radius); // If the location just left the query, fire the 'key_exited' callbacks @@ -467,7 +466,7 @@ export class GeoQuery { const locationDict = this._locationsTracked[key]; delete this._locationsTracked[key]; if (typeof locationDict !== 'undefined' && locationDict.isInQuery) { - const distanceFromCenter: number = (currentLocation) ? GeoFire.distance(currentLocation, this._center) : null; + const distanceFromCenter: number = (currentLocation) ? distance(currentLocation, this._center) : null; this._fireCallbacksForKey('key_exited', key, currentLocation, distanceFromCenter); } } @@ -504,7 +503,7 @@ export class GeoQuery { const oldLocation: number[] = (key in this._locationsTracked) ? this._locationsTracked[key].location : null; // Determine if the location is within this query - distanceFromCenter = GeoFire.distance(location, this._center); + distanceFromCenter = distance(location, this._center); isInQuery = (distanceFromCenter <= this._radius); // Add this location to the locations queried dictionary even if it is not within this query diff --git a/src/geoFire/index.ts b/src/geoFire/index.ts index 00fa3d57..ee1309cd 100644 --- a/src/geoFire/index.ts +++ b/src/geoFire/index.ts @@ -13,7 +13,7 @@ import * as firebase from 'firebase'; import { GeoQuery } from './geoQuery'; -import { decodeGeoFireObject, degreesToRadians, encodeGeoFireObject, encodeGeohash, validateLocation, validateKey } from './geoFireUtils'; +import { decodeGeoFireObject, degreesToRadians, distance, encodeGeoFireObject, encodeGeohash, validateLocation, validateKey } from './geoFireUtils'; import { QueryCriteria } from './interfaces'; @@ -141,20 +141,7 @@ export class GeoFire { * @param location2 The [latitude, longitude] pair of the second location. * @returns The distance, in kilometers, between the inputted locations. */ - static distance(location1: number[], location2: number[]) { - validateLocation(location1); - validateLocation(location2); - - var radius = 6371; // Earth's radius in kilometers - var latDelta = degreesToRadians(location2[0] - location1[0]); - var lonDelta = degreesToRadians(location2[1] - location1[1]); - - var a = (Math.sin(latDelta / 2) * Math.sin(latDelta / 2)) + - (Math.cos(degreesToRadians(location1[0])) * Math.cos(degreesToRadians(location2[0])) * - Math.sin(lonDelta / 2) * Math.sin(lonDelta / 2)); - - var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - - return radius * c; + static distance(location1: number[], location2: number[]): number { + return distance(location1, location2); }; } From a607d6e9135d4412b3cb30158ac2365e17befe23 Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Mon, 11 Jun 2018 23:28:14 -0700 Subject: [PATCH 19/23] refactor: introduce new interfaces and use maps instead of dicts --- package.json | 3 +- rollup.config.js | 4 - src/geoFire/geoFireUtils.ts | 60 +++++------- src/geoFire/geoQuery.ts | 103 ++++++++++---------- src/geoFire/index.ts | 2 +- src/geoFire/interfaces/geoQueryCallbacks.ts | 6 ++ src/geoFire/interfaces/geoQueryState.ts | 9 ++ src/geoFire/interfaces/index.ts | 3 + src/geoFire/interfaces/locationTracked.ts | 6 ++ src/index.ts | 2 +- test/tests/geoFireUtils.test.ts | 2 + 11 files changed, 104 insertions(+), 96 deletions(-) create mode 100644 src/geoFire/interfaces/geoQueryCallbacks.ts create mode 100644 src/geoFire/interfaces/geoQueryState.ts create mode 100644 src/geoFire/interfaces/locationTracked.ts diff --git a/package.json b/package.json index 544c5810..19e2a354 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,7 @@ "realtime", "geolocation" ], - "main": "dist/index.node.cjs.js", - "browser": "dist/index.cjs.js", + "main": "dist/index.cjs.js", "module": "dist/index.esm.js", "typings": "dist/index.d.ts", "files": [ diff --git a/rollup.config.js b/rollup.config.js index 58c27940..ba12d2ed 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -19,10 +19,6 @@ const external = Object.keys(pkg.dependencies || {}); const completeBuilds = [{ input: 'src/index.ts', output: [{ - file: pkg.browser, - format: 'cjs' - }, - { file: pkg.main, format: 'cjs' }, diff --git a/src/geoFire/geoFireUtils.ts b/src/geoFire/geoFireUtils.ts index 0376a83c..9fd4e289 100644 --- a/src/geoFire/geoFireUtils.ts +++ b/src/geoFire/geoFireUtils.ts @@ -1,6 +1,6 @@ import * as firebase from 'firebase'; -import { GeoFireObj } from './interfaces'; +import { GeoFireObj, QueryCriteria } from './interfaces'; // Default geohash length export const g_GEOHASH_PRECISION: number = 10; @@ -126,7 +126,7 @@ export function validateGeohash(geohash: string): void { * @param newQueryCriteria The criteria which specifies the query's center and/or radius. * @param requireCenterAndRadius The criteria which center and radius required. */ -export function validateCriteria(newQueryCriteria: any, requireCenterAndRadius: boolean = false): void { +export function validateCriteria(newQueryCriteria: QueryCriteria, requireCenterAndRadius: boolean = false): void { if (typeof newQueryCriteria !== 'object') { throw new Error('query criteria must be an object'); } else if (typeof newQueryCriteria.center === 'undefined' && typeof newQueryCriteria.radius === 'undefined') { @@ -437,41 +437,27 @@ export function geoFireGetKey(snapshot: firebase.database.DataSnapshot): string } /** - * Returns the id of a Firestore snapshot across SDK versions. + * Method which calculates the distance, in kilometers, between two locations, + * via the Haversine formula. Note that this is approximate due to the fact that the + * Earth's radius varies between 6356.752 km and 6378.137 km. * - * @param A Firestore snapshot. - * @returns The Firestore snapshot's id. + * @param location1 The [latitude, longitude] pair of the first location. + * @param location2 The [latitude, longitude] pair of the second location. + * @returns The distance, in kilometers, between the inputted locations. */ -export function geoFirestoreGetKey(snapshot: firebase.firestore.DocumentSnapshot): string { - let id: string; - if (typeof snapshot.id === 'string' || snapshot.id === null) { - id = snapshot.id; - } - return id; -} +export function distance(location1: number[], location2: number[]): number { + validateLocation(location1); + validateLocation(location2); + + var radius = 6371; // Earth's radius in kilometers + var latDelta = degreesToRadians(location2[0] - location1[0]); + var lonDelta = degreesToRadians(location2[1] - location1[1]); + + var a = (Math.sin(latDelta / 2) * Math.sin(latDelta / 2)) + + (Math.cos(degreesToRadians(location1[0])) * Math.cos(degreesToRadians(location2[0])) * + Math.sin(lonDelta / 2) * Math.sin(lonDelta / 2)); + + var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - /** - * Method which calculates the distance, in kilometers, between two locations, - * via the Haversine formula. Note that this is approximate due to the fact that the - * Earth's radius varies between 6356.752 km and 6378.137 km. - * - * @param location1 The [latitude, longitude] pair of the first location. - * @param location2 The [latitude, longitude] pair of the second location. - * @returns The distance, in kilometers, between the inputted locations. - */ - export function distance(location1: number[], location2: number[]): number { - validateLocation(location1); - validateLocation(location2); - - var radius = 6371; // Earth's radius in kilometers - var latDelta = degreesToRadians(location2[0] - location1[0]); - var lonDelta = degreesToRadians(location2[1] - location1[1]); - - var a = (Math.sin(latDelta / 2) * Math.sin(latDelta / 2)) + - (Math.cos(degreesToRadians(location1[0])) * Math.cos(degreesToRadians(location2[0])) * - Math.sin(lonDelta / 2) * Math.sin(lonDelta / 2)); - - var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - - return radius * c; - }; \ No newline at end of file + return radius * c; +}; \ No newline at end of file diff --git a/src/geoFire/geoQuery.ts b/src/geoFire/geoQuery.ts index 4935280b..b9d1f795 100644 --- a/src/geoFire/geoQuery.ts +++ b/src/geoFire/geoQuery.ts @@ -3,44 +3,45 @@ import * as firebase from 'firebase'; import { GeoCallbackRegistration } from './geoCallbackRegistration'; import { decodeGeoFireObject, distance, encodeGeohash, geoFireGetKey, geohashQueries, validateCriteria, validateLocation } from './geoFireUtils'; -import { QueryCriteria } from './interfaces'; +import { GeoQueryCallbacks, GeoQueryState, LocationTracked, QueryCriteria } from './interfaces'; /** * Creates a GeoQuery instance. */ export class GeoQuery { // Event callbacks - private _callbacks: any = { ready: [], key_entered: [], key_exited: [], key_moved: [] }; + private _callbacks: GeoQueryCallbacks = { ready: [], key_entered: [], key_exited: [], key_moved: [] }; // Variable to track when the query is cancelled private _cancelled: boolean = false; private _center: number[]; // A dictionary of geohash queries which currently have an active callbacks - private _currentGeohashesQueried: any = {}; + private _currentGeohashesQueried: Map = new Map(); // A dictionary of locations that a currently active in the queries // Note that not all of these are currently within this query - private _locationsTracked: any = {}; + private _locationsTracked: Map = new Map(); private _radius: number; // Variables used to keep track of when to fire the 'ready' event private _valueEventFired: boolean = false; private _outstandingGeohashReadyEvents: any; - // Every ten seconds, clean up the geohashes we are currently querying for. We keep these around - // for a little while since it's likely that they will need to be re-queried shortly after they - // move outside of the query's bounding box. + private _geohashCleanupScheduled: boolean = false; - private _cleanUpCurrentGeohashesQueriedInterval: NodeJS.Timer; + private _cleanUpCurrentGeohashesQueriedInterval: any; private _cleanUpCurrentGeohashesQueriedTimeout = null; /** * @param _firebaseRef A Firebase reference where the GeoFire data will be stored. - * @param _queryCriteria The criteria which specifies the query's center and radius. + * @param queryCriteria The criteria which specifies the query's center and radius. */ - constructor(private _firebaseRef: firebase.database.Reference, private _queryCriteria: QueryCriteria) { + constructor(private _firebaseRef: firebase.database.Reference, queryCriteria: QueryCriteria) { // Firebase reference of the GeoFire which created this query if (Object.prototype.toString.call(this._firebaseRef) !== '[object Object]') { throw new Error('firebaseRef must be an instance of Firebase'); } + // Every ten seconds, clean up the geohashes we are currently querying for. We keep these around + // for a little while since it's likely that they will need to be re-queried shortly after they + // move outside of the query's bounding box. this._cleanUpCurrentGeohashesQueriedInterval = setInterval(() => { if (this._geohashCleanupScheduled === false) { this._cleanUpCurrentGeohashesQueried(); @@ -48,9 +49,9 @@ export class GeoQuery { }, 10000); // Validate and save the query criteria - validateCriteria(_queryCriteria, true); - this._center = _queryCriteria.center; - this._radius = _queryCriteria.radius; + validateCriteria(queryCriteria, true); + this._center = queryCriteria.center; + this._radius = queryCriteria.radius; // Listen for new geohashes being added around this query and fire the appropriate events this._listenForNewGeohashes(); @@ -71,15 +72,15 @@ export class GeoQuery { this._callbacks = { ready: [], key_entered: [], key_exited: [], key_moved: [] }; // Turn off all Firebase listeners for the current geohashes being queried - const keys: string[] = Object.keys(this._currentGeohashesQueried); + const keys: string[] = Array.from(this._currentGeohashesQueried.keys()); keys.forEach((geohashQueryStr: string) => { const query: string[] = this._stringToQuery(geohashQueryStr); - this._cancelGeohashQuery(query, this._currentGeohashesQueried[geohashQueryStr]); - delete this._currentGeohashesQueried[geohashQueryStr]; + this._cancelGeohashQuery(query, this._currentGeohashesQueried.get(geohashQueryStr)); + this._currentGeohashesQueried.delete(geohashQueryStr); }); // Delete any stored locations - this._locationsTracked = {}; + this._locationsTracked = new Map(); // Turn off the current geohashes queried clean up interval clearInterval(this._cleanUpCurrentGeohashesQueriedInterval); @@ -137,9 +138,9 @@ export class GeoQuery { // If this is a 'key_entered' callback, fire it for every location already within this query if (eventType === 'key_entered') { - const keys: string[] = Object.keys(this._locationsTracked); + const keys: string[] = Array.from(this._locationsTracked.keys()); keys.forEach((key: string) => { - const locationDict = this._locationsTracked[key]; + const locationDict = this._locationsTracked.get(key); if (typeof locationDict !== 'undefined' && locationDict.isInQuery) { callback(key, locationDict.location, locationDict.distanceFromCenter); } @@ -179,7 +180,7 @@ export class GeoQuery { // Loop through all of the locations in the query, update their distance from the center of the // query, and fire any appropriate events - const keys: string[] = Object.keys(this._locationsTracked); + const keys: string[] = Array.from(this._locationsTracked.keys()); for (const key of keys) { // If the query was cancelled while going through this loop, stop updating locations and stop // firing events @@ -187,7 +188,7 @@ export class GeoQuery { break; } // Get the cached information for this location - const locationDict = this._locationsTracked[key]; + const locationDict = this._locationsTracked.get(key); // Save if the location was already in the query const wasAlreadyInQuery = locationDict.isInQuery; // Update the location's distance to the new query center @@ -220,7 +221,7 @@ export class GeoQuery { * @param query The geohash query. * @param queryState An object storing the current state of the query. */ - private _cancelGeohashQuery(query: string[], queryState: any): void { + private _cancelGeohashQuery(query: string[], queryState: GeoQueryState): void { const queryRef = this._firebaseRef.orderByChild('g').startAt(query[0]).endAt(query[1]); queryRef.off('child_added', queryState.childAddedCallback); queryRef.off('child_removed', queryState.childRemovedCallback); @@ -253,7 +254,7 @@ export class GeoQuery { */ private _childRemovedCallback(locationDataSnapshot: firebase.database.DataSnapshot): void { const key: string = geoFireGetKey(locationDataSnapshot); - if (key in this._locationsTracked) { + if (this._locationsTracked.has(key)) { this._firebaseRef.child(key).once('value', (snapshot: firebase.database.DataSnapshot) => { const location: number[] = (snapshot.val() === null) ? null : decodeGeoFireObject(snapshot.val()); const geohash: string = (location !== null) ? encodeGeohash(location) : null; @@ -271,25 +272,25 @@ export class GeoQuery { * Removes unnecessary Firebase queries which are currently being queried. */ private _cleanUpCurrentGeohashesQueried(): void { - let keys: string[] = Object.keys(this._currentGeohashesQueried); + let keys: string[] = Array.from(this._currentGeohashesQueried.keys()); keys.forEach((geohashQueryStr: string) => { - const queryState: any = this._currentGeohashesQueried[geohashQueryStr]; + const queryState: any = this._currentGeohashesQueried.get(geohashQueryStr); if (queryState.active === false) { const query = this._stringToQuery(geohashQueryStr); // Delete the geohash since it should no longer be queried this._cancelGeohashQuery(query, queryState); - delete this._currentGeohashesQueried[geohashQueryStr]; + this._currentGeohashesQueried.delete(geohashQueryStr); } }); // Delete each location which should no longer be queried - keys = Object.keys(this._locationsTracked); + keys = Array.from(this._locationsTracked.keys()); keys.forEach((key: string) => { - if (!this._geohashInSomeQuery(this._locationsTracked[key].geohash)) { - if (this._locationsTracked[key].isInQuery) { + if (!this._geohashInSomeQuery(this._locationsTracked.get(key).geohash)) { + if (this._locationsTracked.get(key).isInQuery) { throw new Error('Internal State error, trying to remove location that is still in query'); } - delete this._locationsTracked[key]; + this._locationsTracked.delete(key); } }); @@ -337,9 +338,9 @@ export class GeoQuery { * @returns Returns true if the geohash is part of any of the current geohash queries. */ private _geohashInSomeQuery(geohash: string): boolean { - const keys: string[] = Object.keys(this._currentGeohashesQueried); + const keys: string[] = Array.from(this._currentGeohashesQueried.keys()); for (const queryStr of keys) { - if (queryStr in this._currentGeohashesQueried) { + if (this._currentGeohashesQueried.has(queryStr)) { const query = this._stringToQuery(queryStr); if (geohash >= query[0] && geohash <= query[1]) { return true; @@ -381,20 +382,20 @@ export class GeoQuery { // For all of the geohashes that we are already currently querying, check if they are still // supposed to be queried. If so, don't re-query them. Otherwise, mark them to be un-queried // next time we clean up the current geohashes queried dictionary. - const keys: string[] = Object.keys(this._currentGeohashesQueried); + const keys: string[] = Array.from(this._currentGeohashesQueried.keys()); keys.forEach((geohashQueryStr: string) => { const index: number = geohashesToQuery.indexOf(geohashQueryStr); if (index === -1) { - this._currentGeohashesQueried[geohashQueryStr].active = false; + this._currentGeohashesQueried.get(geohashQueryStr).active = false; } else { - this._currentGeohashesQueried[geohashQueryStr].active = true; + this._currentGeohashesQueried.get(geohashQueryStr).active = true; geohashesToQuery.splice(index, 1); } }); // If we are not already cleaning up the current geohashes queried and we have more than 25 of them, // kick off a timeout to clean them up so we don't create an infinite number of unneeded queries. - if (this._geohashCleanupScheduled === false && Object.keys(this._currentGeohashesQueried).length > 25) { + if (this._geohashCleanupScheduled === false && this._currentGeohashesQueried.size > 25) { this._geohashCleanupScheduled = true; this._cleanUpCurrentGeohashesQueriedTimeout = setTimeout(this._cleanUpCurrentGeohashesQueried, 10); } @@ -426,13 +427,13 @@ export class GeoQuery { }); // Add the geohash query to the current geohashes queried dictionary and save its state - this._currentGeohashesQueried[toQueryStr] = { + this._currentGeohashesQueried.set(toQueryStr, { active: true, - childAddedCallback: childAddedCallback, - childRemovedCallback: childRemovedCallback, - childChangedCallback: childChangedCallback, - valueCallback: valueCallback - }; + childAddedCallback, + childRemovedCallback, + childChangedCallback, + valueCallback + }); }); // Based upon the algorithm to calculate geohashes, it's possible that no 'new' // geohashes were queried even if the client updates the radius of the query. @@ -463,8 +464,8 @@ export class GeoQuery { * @param currentLocation The current location as [latitude, longitude] pair or null if removed. */ private _removeLocation(key: string, currentLocation?: number[]): void { - const locationDict = this._locationsTracked[key]; - delete this._locationsTracked[key]; + const locationDict = this._locationsTracked.get(key); + this._locationsTracked.delete(key); if (typeof locationDict !== 'undefined' && locationDict.isInQuery) { const distanceFromCenter: number = (currentLocation) ? distance(currentLocation, this._center) : null; this._fireCallbacksForKey('key_exited', key, currentLocation, distanceFromCenter); @@ -499,20 +500,20 @@ export class GeoQuery { validateLocation(location); // Get the key and location let distanceFromCenter: number, isInQuery; - const wasInQuery: boolean = (key in this._locationsTracked) ? this._locationsTracked[key].isInQuery : false; - const oldLocation: number[] = (key in this._locationsTracked) ? this._locationsTracked[key].location : null; + const wasInQuery: boolean = (this._locationsTracked.has(key)) ? this._locationsTracked.get(key).isInQuery : false; + const oldLocation: number[] = (this._locationsTracked.has(key)) ? this._locationsTracked.get(key).location : null; // Determine if the location is within this query distanceFromCenter = distance(location, this._center); isInQuery = (distanceFromCenter <= this._radius); // Add this location to the locations queried dictionary even if it is not within this query - this._locationsTracked[key] = { - location: location, - distanceFromCenter: distanceFromCenter, - isInQuery: isInQuery, + this._locationsTracked.set(key, { + location, + distanceFromCenter, + isInQuery, geohash: encodeGeohash(location) - }; + }); // Fire the 'key_entered' event if the provided key has entered this query if (isInQuery && !wasInQuery) { diff --git a/src/geoFire/index.ts b/src/geoFire/index.ts index ee1309cd..080cf75e 100644 --- a/src/geoFire/index.ts +++ b/src/geoFire/index.ts @@ -13,7 +13,7 @@ import * as firebase from 'firebase'; import { GeoQuery } from './geoQuery'; -import { decodeGeoFireObject, degreesToRadians, distance, encodeGeoFireObject, encodeGeohash, validateLocation, validateKey } from './geoFireUtils'; +import { decodeGeoFireObject, distance, encodeGeoFireObject, encodeGeohash, validateLocation, validateKey } from './geoFireUtils'; import { QueryCriteria } from './interfaces'; diff --git a/src/geoFire/interfaces/geoQueryCallbacks.ts b/src/geoFire/interfaces/geoQueryCallbacks.ts new file mode 100644 index 00000000..c41ab645 --- /dev/null +++ b/src/geoFire/interfaces/geoQueryCallbacks.ts @@ -0,0 +1,6 @@ +export interface GeoQueryCallbacks { + ready: any[]; + key_entered: any[]; + key_exited: any[]; + key_moved: any[]; +} \ No newline at end of file diff --git a/src/geoFire/interfaces/geoQueryState.ts b/src/geoFire/interfaces/geoQueryState.ts new file mode 100644 index 00000000..fee89011 --- /dev/null +++ b/src/geoFire/interfaces/geoQueryState.ts @@ -0,0 +1,9 @@ +import * as firebase from 'firebase'; + +export interface GeoQueryState { + active: boolean; + childAddedCallback: (a: firebase.database.DataSnapshot, b?: string) => any; + childRemovedCallback: (a: firebase.database.DataSnapshot, b?: string) => any; + childChangedCallback: (a: firebase.database.DataSnapshot, b?: string) => any; + valueCallback: (a: firebase.database.DataSnapshot, b?: string) => any; +} \ No newline at end of file diff --git a/src/geoFire/interfaces/index.ts b/src/geoFire/interfaces/index.ts index 1c3818cc..cbb7a6b6 100644 --- a/src/geoFire/interfaces/index.ts +++ b/src/geoFire/interfaces/index.ts @@ -1,2 +1,5 @@ export * from './geoFireObj'; +export * from './geoQueryCallbacks'; +export * from './geoQueryState'; +export * from './locationTracked'; export * from './queryCriteria'; \ No newline at end of file diff --git a/src/geoFire/interfaces/locationTracked.ts b/src/geoFire/interfaces/locationTracked.ts new file mode 100644 index 00000000..99c285f4 --- /dev/null +++ b/src/geoFire/interfaces/locationTracked.ts @@ -0,0 +1,6 @@ +export interface LocationTracked { + location: number[]; + distanceFromCenter: number; + isInQuery: boolean; + geohash: string; +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index d4748d96..665071ad 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ export { GeoCallbackRegistration } from './geoFire/geoCallbackRegistration'; export { GeoFire } from './geoFire'; export { GeoQuery } from './geoFire/geoQuery'; -export * from './geoFire/interfaces'; \ No newline at end of file +export { GeoFireObj, QueryCriteria } from './geoFire/interfaces'; \ No newline at end of file diff --git a/test/tests/geoFireUtils.test.ts b/test/tests/geoFireUtils.test.ts index 278db116..86ba97da 100644 --- a/test/tests/geoFireUtils.test.ts +++ b/test/tests/geoFireUtils.test.ts @@ -69,6 +69,7 @@ describe('geoFireUtils Tests:', () => { it('validateCriteria(criteria, true) throws errors given invalid query criteria', () => { invalidQueryCriterias.forEach((invalidQueryCriteria) => { + // @ts-ignore expect(() => validateCriteria(invalidQueryCriteria, true)).to.throw(); }); expect(() => validateCriteria({ center: [0, 0] }, true)).to.throw(); @@ -77,6 +78,7 @@ describe('geoFireUtils Tests:', () => { it('validateCriteria(criteria) throws errors given invalid query criteria', () => { invalidQueryCriterias.forEach((invalidQueryCriteria) => { + // @ts-ignore expect(() => validateCriteria(invalidQueryCriteria)).to.throw(); }); }); From 154daa1f3670dae6e0c6b12b10ae91a489a1723b Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Mon, 11 Jun 2018 23:38:11 -0700 Subject: [PATCH 20/23] fix: lock node version for tests and revert geoFireGetKey logic --- .travis.yml | 4 ++-- src/geoFire/geoFireUtils.ts | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index c777e1a6..143470d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,10 @@ language: node_js node_js: - - stable + - 8 sudo: false install: - npm install script: - npm run test after_success: - - npm run coverage + - npm run coverage \ No newline at end of file diff --git a/src/geoFire/geoFireUtils.ts b/src/geoFire/geoFireUtils.ts index 9fd4e289..8a4d3d91 100644 --- a/src/geoFire/geoFireUtils.ts +++ b/src/geoFire/geoFireUtils.ts @@ -429,10 +429,17 @@ export function decodeGeoFireObject(geoFireObj: GeoFireObj): number[] { * @returns The Firebase snapshot's key. */ export function geoFireGetKey(snapshot: firebase.database.DataSnapshot): string { - let key: string; + let key; if (typeof snapshot.key === 'string' || snapshot.key === null) { key = snapshot.key; + } else if (typeof snapshot.key === 'function') { + // @ts-ignore + key = snapshot.key(); + } else { + // @ts-ignore + key = snapshot.name(); } + return key; } From 2859aa8192ac514d2ce90604a17b3ff7bdbb3923 Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Wed, 13 Jun 2018 07:31:23 -0700 Subject: [PATCH 21/23] chore: update dependencies and support firebase 5.x.x --- package-lock.json | 2897 ++++++++++++++------------------------------- package.json | 36 +- rollup.config.js | 2 +- 3 files changed, 909 insertions(+), 2026 deletions(-) diff --git a/package-lock.json b/package-lock.json index d84ced14..32da0090 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,91 +4,211 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.0.0-beta.49", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.49.tgz", + "integrity": "sha1-vs2AVIJzREDJ0TfkbXc0DmTX9Rs=", + "dev": true, + "requires": { + "@babel/highlight": "7.0.0-beta.49" + } + }, + "@babel/generator": { + "version": "7.0.0-beta.49", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.49.tgz", + "integrity": "sha1-6c/9qROZaszseTu8JauRvBnQv3o=", + "dev": true, + "requires": { + "@babel/types": "7.0.0-beta.49", + "jsesc": "2.5.1", + "lodash": "4.17.10", + "source-map": "0.5.7", + "trim-right": "1.0.1" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.0.0-beta.49", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.49.tgz", + "integrity": "sha1-olwRGbnwNSeGcBJuAiXAMEHI3jI=", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "7.0.0-beta.49", + "@babel/template": "7.0.0-beta.49", + "@babel/types": "7.0.0-beta.49" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0-beta.49", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.49.tgz", + "integrity": "sha1-z1Aj8y0q2S0Ic3STnOwJUby1FEE=", + "dev": true, + "requires": { + "@babel/types": "7.0.0-beta.49" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.0.0-beta.49", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.49.tgz", + "integrity": "sha1-QNeO2glo0BGxxShm5XRs+yPldUg=", + "dev": true, + "requires": { + "@babel/types": "7.0.0-beta.49" + } + }, + "@babel/highlight": { + "version": "7.0.0-beta.49", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.49.tgz", + "integrity": "sha1-lr3GtD4TSCASumaRsQGEktOWIsw=", + "dev": true, + "requires": { + "chalk": "2.4.1", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, + "@babel/parser": { + "version": "7.0.0-beta.49", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.49.tgz", + "integrity": "sha1-lE0MW6KBK7FZ7b0iZ0Ov0mUXm9w=", + "dev": true + }, + "@babel/template": { + "version": "7.0.0-beta.49", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.49.tgz", + "integrity": "sha1-44q+ghfLl5P0YaUwbXrXRdg+HSc=", + "dev": true, + "requires": { + "@babel/code-frame": "7.0.0-beta.49", + "@babel/parser": "7.0.0-beta.49", + "@babel/types": "7.0.0-beta.49", + "lodash": "4.17.10" + } + }, + "@babel/traverse": { + "version": "7.0.0-beta.49", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.49.tgz", + "integrity": "sha1-TypzaCoYM07WYl0QCo0nMZ98LWg=", + "dev": true, + "requires": { + "@babel/code-frame": "7.0.0-beta.49", + "@babel/generator": "7.0.0-beta.49", + "@babel/helper-function-name": "7.0.0-beta.49", + "@babel/helper-split-export-declaration": "7.0.0-beta.49", + "@babel/parser": "7.0.0-beta.49", + "@babel/types": "7.0.0-beta.49", + "debug": "3.1.0", + "globals": "11.5.0", + "invariant": "2.2.4", + "lodash": "4.17.10" + } + }, + "@babel/types": { + "version": "7.0.0-beta.49", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.49.tgz", + "integrity": "sha1-t+Oxw/TUz+Eb34yJ8e/V4WF7h6Y=", + "dev": true, + "requires": { + "esutils": "2.0.2", + "lodash": "4.17.10", + "to-fast-properties": "2.0.0" + } + }, "@firebase/app": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.2.0.tgz", - "integrity": "sha512-hu+HGRXER3URCb8sZdd4f3QzvVWfuKvJoqd1mlEx9vWQ0fMpFqMeYgOaO7+x7PkNzamMHetItfws5TK9pmnStQ==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.3.3.tgz", + "integrity": "sha512-V5fMC2Ysx1TlHD6x7vj7EOtoyJSU/ts+fp9qxt0E3TA+DbWgKFrkcL+o2jZhi30h0sXKV7oW0vh67YZdZylqOg==", "dev": true, "requires": { - "@firebase/app-types": "0.2.0", - "@firebase/util": "0.1.11", - "tslib": "1.9.0" + "@firebase/app-types": "0.3.2", + "@firebase/util": "0.2.1", + "dom-storage": "2.1.0", + "tslib": "1.9.0", + "xmlhttprequest": "1.8.0" } }, "@firebase/app-types": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.2.0.tgz", - "integrity": "sha512-0hTbuFmTr5sBvn9r2w5nk9FeMkuscj+ClPUBimtOkOnoLvIWRICILRiPTV1CX+7Th4EJ7y17UWx8jieY0f8GtA==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.3.2.tgz", + "integrity": "sha512-ZD8lTgW07NGgo75bTyBJA8Lt9+NweNzot7lrsBtIvfciwUzaFJLsv2EShqjBeuhF7RpG6YFucJ6m67w5buCtzw==", "dev": true }, "@firebase/auth": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.4.2.tgz", - "integrity": "sha512-gMEKuKDdO2oB9c2zmj6NlFOY1ynzummXuUWz6cFgUdNd/Pw5ZpkOlGzS7yxZ7NI0dZXV/xccamN7CN9/vol+cA==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.5.3.tgz", + "integrity": "sha512-/oxprMSyOhc4HXFBqNnBrY26e5DyTfSacyw7xBGuW1zMQrZuUwPM+6Cr0vLuParln28bmRXIinM5g2R9zVdchw==", "dev": true, "requires": { - "@firebase/auth-types": "0.2.1" + "@firebase/auth-types": "0.3.3" } }, "@firebase/auth-types": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.2.1.tgz", - "integrity": "sha512-zjObje4RYJ7RJHt1z8A6xGnfLw/5RwUvk7AHrYwS4N3Mq17j/ZumBvYlAQz4k8sNe9GFDmjmHa30ezFWOglLEQ==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.3.3.tgz", + "integrity": "sha512-7DJwHDjZMrRHuMB3y2z6HukaLt42I1GcUIQ2wO96G3NxpAUkQ1bDlMguwymFk0UFrPsdi3lv1mEwxCGTRNHAiA==", "dev": true }, "@firebase/database": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.2.2.tgz", - "integrity": "sha512-iTNEN33D3V0hAG2hdx+guFBXaN4hcFS2k2EGp/bzNviAG7n2AotMscdbkS6xDS2e3Uk2/D3lfibHQO4zgJ3LIg==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.3.3.tgz", + "integrity": "sha512-8r/snlMuOB6QXdkfjbZaFxK1U8CbQgCG0rVfMHclRVNQhZLtTEtiVNDTQk75bfxY0k+iqyg+6fsNWClA92glcg==", "dev": true, "requires": { - "@firebase/database-types": "0.2.1", + "@firebase/database-types": "0.3.2", "@firebase/logger": "0.1.1", - "@firebase/util": "0.1.11", + "@firebase/util": "0.2.1", "faye-websocket": "0.11.1", "tslib": "1.9.0" } }, "@firebase/database-types": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.2.1.tgz", - "integrity": "sha512-LyvTpLImnhSTyHfPGcBxhD0tHw+R7FUb+als23Ad5hPCcGxlRgLhA+ukrhFIGA8Mt8FYHWgFm7TCX4YDRDxK6w==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.3.2.tgz", + "integrity": "sha512-9ZYdvYQ6r3aaHJarhUM5Hf6lQWu3ZJme+RR0o8qfBb9L04TL3uNjt+AJFku1ysVPntTn+9GqJjiIB2/OC3JtwA==", "dev": true }, "@firebase/firestore": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-0.4.1.tgz", - "integrity": "sha512-RsceKkGQyiTJq/9odss6v+6KgJVLr5H8hgVO/u+d1Is20cQXJFpDjrgcoGairVb4S/jav2LV1/bHXj3HVLObvw==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-0.5.4.tgz", + "integrity": "sha512-OdeF8vU+rn7aklumjc9ga9+QnT+LSl+2uqgLE30wdCpYTEKI6kpb5NxhIqZ+dEspdpgW6xQPM6Uy2IKbJAsjBQ==", "dev": true, "requires": { - "@firebase/firestore-types": "0.3.0", + "@firebase/firestore-types": "0.4.3", "@firebase/logger": "0.1.1", "@firebase/webchannel-wrapper": "0.2.8", - "grpc": "1.10.1", + "grpc": "1.11.3", "tslib": "1.9.0" } }, "@firebase/firestore-types": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-0.3.0.tgz", - "integrity": "sha512-pWqIALmvp91ELeFiiAOIDVvcCxkLxXffp5KYNL1I/mNgqe2CyGoNB6+pUb/S+FH3oCh/HWHBW+0lXDszcP42aA==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-0.4.3.tgz", + "integrity": "sha512-QdFBhH0Bcw7TRodN6nJ1pQq0AGAgMfpIUQguKcogTE/2L/lAECxbUfTWtBcGenKcSKpae5xJuuhGZxaPGkQv7A==", "dev": true }, "@firebase/functions": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.1.1.tgz", - "integrity": "sha512-olSryAPNcH47WFCUmooNgO4P61dmlUEPVYOa0urTLiQh+8M8E7RuDWcqxxxiPk4BCN/l2ZeO0uCDsrzBMX4OqA==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.2.4.tgz", + "integrity": "sha512-HSa2MdBRPWltv1gvE1nWmbzxtWt5jJt4gZ8wje5qEIld7tbLvaWCJRvLGwH/dX1r9s6u6GtDF9FD4Gn2z2xFiQ==", "dev": true, "requires": { - "@firebase/functions-types": "0.1.1", - "@firebase/messaging-types": "0.1.3", - "isomorphic-fetch": "2.2.1" + "@firebase/functions-types": "0.1.3", + "@firebase/messaging-types": "0.2.3", + "isomorphic-fetch": "2.2.1", + "tslib": "1.9.0" } }, "@firebase/functions-types": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.1.1.tgz", - "integrity": "sha512-DMCQAuSafYChef2Wa4yYNPeToEDggnlaUHvseKxPzUwOVVmPdr9dxmqQW+UKeGYeUHfiTWFzvsrk8u1ifyvTZA==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.1.3.tgz", + "integrity": "sha512-9cOrF5b2B3kfdwsRm3owSXZciIQbupbEKhq4WTsLRXU8mz8ol7WxuOuG/FwYaHryeI8z29gIzP9NpiGoKJgo4w==", "dev": true }, "@firebase/logger": { @@ -98,26 +218,26 @@ "dev": true }, "@firebase/messaging": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.2.4.tgz", - "integrity": "sha512-bgnIhKETgA8ZgWXFmF0+RLqr93/nxqnncRndxNcKTPyq4syah703NeTtaJCMR3LWrfKGV9blpEBogtX3JmJK1g==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.3.4.tgz", + "integrity": "sha512-MWewMZdHs7odZ4nTY9QVEQtq2g6dEw9LtSg63Z5FLxN/Mlj+GtEmUnoerprhzR7eo80DCH1Xd++tMmGk4rIz3g==", "dev": true, "requires": { - "@firebase/messaging-types": "0.1.3", - "@firebase/util": "0.1.11", + "@firebase/messaging-types": "0.2.3", + "@firebase/util": "0.2.1", "tslib": "1.9.0" } }, "@firebase/messaging-types": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@firebase/messaging-types/-/messaging-types-0.1.3.tgz", - "integrity": "sha512-kDixnQujUuwvqc12iFLd6ygb+cdCCXCNeJpQD7jV0mnYMbE1Sp4Y7sl2G7N7r0kcoJPHUQHERBkZlw2N3xyJCw==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/messaging-types/-/messaging-types-0.2.3.tgz", + "integrity": "sha512-avwCgZzcx2uxIW/wT3p3G/EyHftIrvMyiTS7AA7dxDlzfx+8dpAeTsb1+jsHJT4F6foSh5HG17Nw8sDzYuxH1Q==", "dev": true }, "@firebase/polyfill": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.3.1.tgz", - "integrity": "sha512-TkmIU4OZeE+rZoQFvq9qYsHv3IYeWahvPUTknUiqN14XJ1777ZEZ+oHBM116/R/Ej3kA5EZxhahqlRHOOD/Dwg==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.3.3.tgz", + "integrity": "sha512-xs8IZf1WEbufYXyfV8YjmiFZOaujRRq0T03NteihYfuGVTTym7z5SmvLvEHLEUjf2fgeobPEzZ2JgrCQHS+QHw==", "dev": true, "requires": { "core-js": "2.5.5", @@ -126,25 +246,25 @@ } }, "@firebase/storage": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.1.9.tgz", - "integrity": "sha512-CrlreoXIlWNAAHnOAlP0KpZ2aM8s4QFOcrIhBE3f7vE6h30TuBuhFFra4slUykCuUflyVjBYlea6ph0fJsJr0w==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.2.3.tgz", + "integrity": "sha512-2sq5jckWszW53gfQMkPNc7EumJ92oErRhzGJANbVzBumwR8qwKZU8/I+/uV9SPK1tVmSUc3S21jdoW5oOJVEuA==", "dev": true, "requires": { - "@firebase/storage-types": "0.1.3", + "@firebase/storage-types": "0.2.3", "tslib": "1.9.0" } }, "@firebase/storage-types": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.1.3.tgz", - "integrity": "sha512-xM939ObH7MLDhhrEi/UyEDj87b3AGypGp5YPpEOwvtL2KpF7VQf7g715LoaWmhGd4+e1MHFMCktQbejkpkj63A==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.2.3.tgz", + "integrity": "sha512-RaZeam2LgsB7xwAtOQr4G0Geoyf7D5TnLF3a12By6Rh0Z9PqBSlWn0SVYGW3SkmxIdqvWZMZvCyamUlqQvQzWw==", "dev": true }, "@firebase/util": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.1.11.tgz", - "integrity": "sha512-xUMugOJBSKVKOjrKJIVeIr4Z/6iDxSuOlOJRdz0xsOBJ9+lZVxGZs0U4oZmszWhQER1zzR+EQWIYFYePt6/QMQ==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.2.1.tgz", + "integrity": "sha512-KPNcIK5+bUUBMII87NqGu+tRUnMcY95xujS2z0QyAfoQCKe11DMHICv3M6uweiLSXqdQwrMTyFtiql1q+0UOYQ==", "dev": true, "requires": { "tslib": "1.9.0" @@ -156,15 +276,6 @@ "integrity": "sha512-ToJbeJnxDc3O325FvcKVb3yHO1hvgHjCFvhKol6Z17GiB7vL104POjFQT4RnlLiAGSRCBAMxinDec9y9vQYdyg==", "dev": true }, - "@types/acorn": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.3.tgz", - "integrity": "sha512-gou/kWQkGPMZjdCKNZGDpqxLm9+ErG/pFZKPX4tvCjr0Xf4FCYYX3nAsu7aDVKJV3KUe27+mvqqyWT/9VZoM/A==", - "dev": true, - "requires": { - "@types/estree": "0.0.39" - } - }, "@types/chai": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.3.tgz", @@ -178,15 +289,15 @@ "dev": true }, "@types/mocha": { - "version": "2.2.48", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.48.tgz", - "integrity": "sha512-nlK/iyETgafGli8Zh9zJVCTicvU3iajSkRwOh3Hhiva598CMqNJ4NcVCGMTGKpGpTYj/9R8RLzS9NAykSSCqGw==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.2.tgz", + "integrity": "sha512-tfg9rh2qQhBW6SBqpvfqTgU6lHWGhQURoTrn7NeDF+CEkO9JGYbkzU23EXu6p3bnvDxLeeSX8ohAA23urvWeNw==", "dev": true }, "@types/node": { - "version": "9.6.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.12.tgz", - "integrity": "sha512-2Z8ziWjJbvV8hHL5YcqCG9ng+/qwUlO1gB4frBD7QI5Wm1Y1iM+AEkGVEv0S5P+aDMwTtAhPJFR4rp1uqagSig==", + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.3.2.tgz", + "integrity": "sha512-9NfEUDp3tgRhmoxzTpTo+lq+KIVFxZahuRX0LHF/9IzKHaWuoWsIrrJ61zw5cnnlGINX8lqJzXYfQTOICS5Q+A==", "dev": true }, "abab": { @@ -196,27 +307,18 @@ "dev": true }, "acorn": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", - "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.6.2.tgz", + "integrity": "sha512-zUzo1E5dI2Ey8+82egfnttyMlMZ2y0D8xOCO3PNPPlYXpl8NZvF6Qk9L9BEtJs+43FqEmfBViDqc5d1ckRDguw==", "dev": true }, - "acorn-dynamic-import": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", - "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", - "dev": true, - "requires": { - "acorn": "5.5.3" - } - }, "acorn-globals": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz", "integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==", "dev": true, "requires": { - "acorn": "5.5.3" + "acorn": "5.6.2" } }, "ajv": { @@ -243,7 +345,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "1.9.2" } }, "argparse": { @@ -394,15 +496,6 @@ "tweetnacl": "0.14.5" } }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "dev": true, - "requires": { - "hoek": "4.2.1" - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -437,9 +530,9 @@ "dev": true }, "buffer-from": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", - "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", + "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==", "dev": true }, "builtin-modules": { @@ -492,23 +585,6 @@ "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.4.0" - }, - "dependencies": { - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "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" - } - } } }, "check-error": { @@ -541,18 +617,18 @@ "dev": true }, "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", + "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "1.1.1" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", "dev": true }, "colour": { @@ -571,9 +647,9 @@ } }, "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "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": { @@ -600,31 +676,11 @@ "integrity": "sha512-FAzXwiDOYLGDWH+zgoIA+8GbWv50hlx+kpEJyvzLKOdnIBv9uWoVl4DhqGgyUHpiRjAlF8KYZSipWXYtllWH6Q==", "dev": true, "requires": { - "js-yaml": "3.11.0", + "js-yaml": "3.12.0", "lcov-parse": "0.0.10", "log-driver": "1.2.7", "minimist": "1.2.0", - "request": "2.85.0" - } - }, - "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "dev": true, - "requires": { - "boom": "5.2.0" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "dev": true, - "requires": { - "hoek": "4.2.1" - } - } + "request": "2.87.0" } }, "cssom": { @@ -634,9 +690,9 @@ "dev": true }, "cssstyle": { - "version": "0.2.37", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz", - "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.3.1.tgz", + "integrity": "sha512-tNvaxM5blOnxanyxI6panOsnfiyLRj3HV4qjqqS45WPNS1usdYWRUQjqTEEELK73lpeP/1KoIGYUwrBn/VcECA==", "dev": true, "requires": { "cssom": "0.3.2" @@ -659,16 +715,7 @@ "requires": { "abab": "1.0.4", "whatwg-mimetype": "2.1.0", - "whatwg-url": "6.4.1" - } - }, - "date-time": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/date-time/-/date-time-2.1.0.tgz", - "integrity": "sha512-/9+C44X7lot0IeiyfgJmETtRMhBidBYM2QFFIkGa0U1k+hSyY87Nw7PY3eDqpvCBm7I3WCSfPeZskW/YYq6m4g==", - "dev": true, - "requires": { - "time-zone": "1.0.0" + "whatwg-url": "6.5.0" } }, "debug": { @@ -744,7 +791,7 @@ "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", "dev": true, "requires": { - "iconv-lite": "0.4.22" + "iconv-lite": "0.4.23" } }, "escape-string-regexp": { @@ -754,9 +801,9 @@ "dev": true }, "escodegen": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz", - "integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.10.0.tgz", + "integrity": "sha512-fjUOf8johsv23WuIKdNQU4P9t9jhQ4Qzx6pC2uW890OloK3Zs1ZAoCNpg/2larNF501jLl3UNy0kIRcF6VI22g==", "dev": true, "requires": { "esprima": "3.1.3", @@ -787,9 +834,9 @@ "dev": true }, "estree-walker": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.3.1.tgz", - "integrity": "sha1-5rGlHPcpJSTnI3wxLl/mZgwc4ao=", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.5.2.tgz", + "integrity": "sha512-XpCnW/AE10ws/kDAs37cngSkvgIR8aN3G0MS85m7dUpuK2EREo9VJ00uvw6Dg/hXEpfsE1I1TvJOJr+Z+TL+ig==", "dev": true }, "esutils": { @@ -813,7 +860,7 @@ "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { - "fill-range": "2.2.3" + "fill-range": "2.2.4" } }, "extend": { @@ -871,34 +918,32 @@ "dev": true }, "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", "dev": true, "requires": { "is-number": "2.1.0", "isobject": "2.1.0", - "randomatic": "1.1.7", + "randomatic": "3.0.0", "repeat-element": "1.1.2", "repeat-string": "1.6.1" } }, "firebase": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-4.13.1.tgz", - "integrity": "sha512-vi+DbUPRrsUaLxuMLgCuyISTBNQ/TqrEwWzUpMqyABPwE6YhGplNnCDPs+/kRDXxdqWUdwrUEXo1ansBSimoNw==", - "dev": true, - "requires": { - "@firebase/app": "0.2.0", - "@firebase/auth": "0.4.2", - "@firebase/database": "0.2.2", - "@firebase/firestore": "0.4.1", - "@firebase/functions": "0.1.1", - "@firebase/messaging": "0.2.4", - "@firebase/polyfill": "0.3.1", - "@firebase/storage": "0.1.9", - "dom-storage": "2.1.0", - "xmlhttprequest": "1.8.0" + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-5.0.4.tgz", + "integrity": "sha512-UIlUOcISw6uiAwbsj/bamDDKJtdjx9GU2m9XgPF58DSA8IJaIbfdL6jT5C03R5mL+mMpXoKDAOeOq+ynf+R09w==", + "dev": true, + "requires": { + "@firebase/app": "0.3.3", + "@firebase/auth": "0.5.3", + "@firebase/database": "0.3.3", + "@firebase/firestore": "0.5.4", + "@firebase/functions": "0.2.4", + "@firebase/messaging": "0.3.4", + "@firebase/polyfill": "0.3.3", + "@firebase/storage": "0.2.3" } }, "for-in": { @@ -998,6 +1043,12 @@ "is-glob": "2.0.1" } }, + "globals": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.5.0.tgz", + "integrity": "sha512-hYyf+kI8dm3nORsiiXUQigOU62hDLfJ9G01uyGMxhc6BKsircrUhC4uJPQPUSuq2GrTmiiEt7ewxlMdBewfmKQ==", + "dev": true + }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", @@ -1005,21 +1056,21 @@ "dev": true }, "growl": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, "grpc": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.10.1.tgz", - "integrity": "sha512-xmhA11h2XhqpSVzDAmoQAYdNQ+swILXpKOiRpAEQ2kX55ioxVADc6v7SkS4zQBxm4klhQHgGqpGKvoL6LGx4VQ==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.11.3.tgz", + "integrity": "sha512-7fJ40USpnP7hxGK0uRoEhJz6unA5VUdwInfwAY2rK2+OVxdDJSdTZQ/8/M+1tW68pHZYgHvg2ohvJ+clhW3ANg==", "dev": true, "requires": { "lodash": "4.17.10", "nan": "2.10.0", - "node-pre-gyp": "0.7.0", - "protobufjs": "5.0.2" + "node-pre-gyp": "0.10.0", + "protobufjs": "5.0.3" }, "dependencies": { "abbrev": { @@ -1028,18 +1079,6 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.1.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" - } - }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -1059,93 +1098,29 @@ "dev": true, "requires": { "delegates": "1.0.0", - "readable-stream": "2.3.5" + "readable-stream": "2.3.6" } }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", - "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 }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "dev": true, - "requires": { - "hoek": "4.2.1" - } - }, "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "chownr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", "dev": true }, "code-point-at": { @@ -1154,15 +1129,6 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, - "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "dev": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1181,35 +1147,6 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, - "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "dev": true, - "requires": { - "boom": "5.2.0" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "dev": true, - "requires": { - "hoek": "4.2.1" - } - } - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "1.0.0" - } - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1220,15 +1157,9 @@ } }, "deep-extend": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", - "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", - "dev": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", + "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", "dev": true }, "delegates": { @@ -1243,55 +1174,13 @@ "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.6", - "mime-types": "2.1.18" + "minipass": "2.2.4" } }, "fs.realpath": { @@ -1300,29 +1189,6 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.2" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", - "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", - "dev": true, - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } - }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -1339,15 +1205,6 @@ "wide-align": "1.1.2" } }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "1.0.0" - } - }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -1362,61 +1219,25 @@ "path-is-absolute": "1.0.1" } }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "dev": true, - "requires": { - "ajv": "5.5.2", - "har-schema": "2.0.0" - } - }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "dev": true, - "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.1", - "sntp": "2.1.0" - } - }, - "hoek": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", - "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", "dev": true }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "ignore-walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.14.1" + "minimatch": "3.0.4" } }, "inflight": { @@ -1450,83 +1271,19 @@ "number-is-nan": "1.0.1" } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "isarray": { + "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", - "dev": true - }, - "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "dev": true, - "requires": { - "mime-db": "1.33.0" - } - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.8" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -1535,6 +1292,25 @@ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, + "minipass": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz", + "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "minizlib": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz", + "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", + "dev": true, + "requires": { + "minipass": "2.2.4" + } + }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -1558,22 +1334,33 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, + "needle": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.1.tgz", + "integrity": "sha512-t/ZswCM9JTWjAdXS9VpvqhI2Ct2sL2MdY4fUXqGJaGBk13ge99ObqRksRTbBE56K+wxUXwwfZYOuZHifFW9q+Q==", + "dev": true, + "requires": { + "debug": "2.6.9", + "iconv-lite": "0.4.19", + "sax": "1.2.4" + } + }, "node-pre-gyp": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.7.0.tgz", - "integrity": "sha1-Va7/uu2TtQ0KRlfUaRmM2ArJ3zY=", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.0.tgz", + "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==", "dev": true, "requires": { "detect-libc": "1.0.3", "mkdirp": "0.5.1", + "needle": "2.2.1", "nopt": "4.0.1", + "npm-packlist": "1.1.10", "npmlog": "4.1.2", - "rc": "1.2.6", - "request": "2.83.0", + "rc": "1.2.7", "rimraf": "2.6.2", "semver": "5.5.0", - "tar": "2.2.1", - "tar-pack": "3.4.1" + "tar": "4.4.2" } }, "nopt": { @@ -1586,6 +1373,22 @@ "osenv": "0.1.5" } }, + "npm-bundled": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.3.tgz", + "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==", + "dev": true + }, + "npm-packlist": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.10.tgz", + "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", + "dev": true, + "requires": { + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" + } + }, "npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", @@ -1604,12 +1407,6 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -1653,46 +1450,28 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", - "dev": true - }, "rc": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.6.tgz", - "integrity": "sha1-6xiYnG1PTxYsOZ953dKfODVWgJI=", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.7.tgz", + "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", "dev": true, "requires": { - "deep-extend": "0.4.2", + "deep-extend": "0.5.1", "ini": "1.3.5", "minimist": "1.2.0", "strip-json-comments": "2.0.1" } }, "readable-stream": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz", - "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "1.0.2", @@ -1700,40 +1479,10 @@ "isarray": "1.0.0", "process-nextick-args": "2.0.0", "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", + "string_decoder": "1.1.1", "util-deprecate": "1.0.2" } }, - "request": { - "version": "2.83.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", - "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", - "dev": true, - "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.6", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.2", - "har-validator": "5.0.3", - "hawk": "6.0.2", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.18", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.1", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.4", - "tunnel-agent": "0.6.0", - "uuid": "3.2.1" - } - }, "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", @@ -1749,6 +1498,12 @@ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", "dev": true }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, "semver": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", @@ -1767,31 +1522,6 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, - "sntp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", - "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", - "dev": true, - "requires": { - "hoek": "4.2.1" - } - }, - "sshpk": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", - "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", - "dev": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - } - }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -1804,20 +1534,14 @@ } }, "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "5.1.1" } }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", - "dev": true - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -1834,86 +1558,34 @@ "dev": true }, "tar": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", - "dev": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "tar-pack": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.1.tgz", - "integrity": "sha512-PPRybI9+jM5tjtCbN2cxmmRU7YmqT3Zv/UDy48tAh2XRkLa9bAORtSWLkVc13+GJF+cdTh1yEnHEk3cpTaL5Kg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.3.5", - "rimraf": "2.6.2", - "tar": "2.2.1", - "uid-number": "0.0.6" - } - }, - "tough-cookie": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.2.tgz", + "integrity": "sha512-BfkE9CciGGgDsATqkikUHrQrraBCO+ke/1f6SFAEMnxyyfN9lxC+nW1NFWMpqH865DhHIy9vQi682gk1X7friw==", "dev": true, "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.2", + "yallist": "3.0.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } } }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", - "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", - "dev": true - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - } - }, "wide-align": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", @@ -1928,6 +1600,12 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true + }, + "yallist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", + "dev": true } } }, @@ -1957,35 +1635,17 @@ } }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "dev": true, - "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.1", - "sntp": "2.1.0" - } - }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, - "hoek": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", - "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", - "dev": true - }, "html-encoding-sniffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", @@ -1996,9 +1656,9 @@ } }, "http-parser-js": { - "version": "0.4.12", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.12.tgz", - "integrity": "sha1-uc+/Sizybw/DSxDKFImid3HjR08=", + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.13.tgz", + "integrity": "sha1-O9bW/ebjFyyTNMOzO2wZPYD+ETc=", "dev": true }, "http-signature": { @@ -2009,13 +1669,13 @@ "requires": { "assert-plus": "1.0.0", "jsprim": "1.4.1", - "sshpk": "1.14.1" + "sshpk": "1.14.2" } }, "iconv-lite": { - "version": "0.4.22", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.22.tgz", - "integrity": "sha512-1AinFBeDTnsvVEP+V1QBlHpM1UZZl7gWB6fcz7B1Ho+LI1dUh2sSrxoCfVt2PinRHzXAziSniEV3P7JbTDHcXA==", + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "dev": true, "requires": { "safer-buffer": "2.1.2" @@ -2037,18 +1697,21 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "1.3.1" + } + }, "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=", "dev": true }, - "irregular-plurals": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-1.4.0.tgz", - "integrity": "sha1-LKmwM2UREYVUEvFr5dd8YqRYp2Y=", - "dev": true - }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -2127,23 +1790,6 @@ "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", "dev": true }, - "is-reference": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.0.tgz", - "integrity": "sha512-h37O/IX4efe56o9k41II1ECMqKwtqHa7/12dLDEzJIFux2x15an4WCDb0/eKdmUgRpLJ3bR0DrzDc7vOrVgRDw==", - "dev": true, - "requires": { - "@types/estree": "0.0.38" - }, - "dependencies": { - "@types/estree": { - "version": "0.0.38", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.38.tgz", - "integrity": "sha512-F/v7t1LwS4vnXuPooJQGBRKRGIoxWUTmA4VHfqjOccFsNDThD5bfUNpITive6s352O7o384wcpEaDV8rHCehDA==", - "dev": true - } - } - }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -2187,6 +1833,27 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, + "istanbul-lib-coverage": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.0.tgz", + "integrity": "sha512-yMSw5xLIbdaxiVXHk3amfNM2WeBxLrwH/BCyZ9HvA/fylwziAIJOG2rKqWyLqEJqwKT725vxxqidv+SyynnGAA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-2.2.0.tgz", + "integrity": "sha512-ozQGtlIw+/a/F3n6QwWiuuyRAPp64+g2GVsKYsIez0sgIEzkU5ZpL2uZ5pmAzbEJ82anlRaPlOQZzkRXspgJyg==", + "dev": true, + "requires": { + "@babel/generator": "7.0.0-beta.49", + "@babel/parser": "7.0.0-beta.49", + "@babel/template": "7.0.0-beta.49", + "@babel/traverse": "7.0.0-beta.49", + "@babel/types": "7.0.0-beta.49", + "istanbul-lib-coverage": "2.0.0", + "semver": "5.5.0" + } + }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -2194,9 +1861,9 @@ "dev": true }, "js-yaml": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", - "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", "dev": true, "requires": { "argparse": "1.0.10", @@ -2211,26 +1878,26 @@ "optional": true }, "jsdom": { - "version": "11.10.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.10.0.tgz", - "integrity": "sha512-x5No5FpJgBg3j5aBwA8ka6eGuS5IxbC8FOkmyccKvObtFT0bDMict/LOxINZsZGZSfGdNomLZ/qRV9Bpq/GIBA==", + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.11.0.tgz", + "integrity": "sha512-ou1VyfjwsSuWkudGxb03FotDajxAto6USAlmMZjE2lc0jCznt7sBWkhfRBRaWwbnmDqdMSTKTLT5d9sBFkkM7A==", "dev": true, "requires": { "abab": "1.0.4", - "acorn": "5.5.3", + "acorn": "5.6.2", "acorn-globals": "4.1.0", "array-equal": "1.0.0", "cssom": "0.3.2", - "cssstyle": "0.2.37", + "cssstyle": "0.3.1", "data-urls": "1.0.0", "domexception": "1.0.1", - "escodegen": "1.9.1", + "escodegen": "1.10.0", "html-encoding-sniffer": "1.0.2", "left-pad": "1.3.0", - "nwmatcher": "1.4.4", + "nwsapi": "2.0.3", "parse5": "4.0.0", "pn": "1.1.0", - "request": "2.85.0", + "request": "2.87.0", "request-promise-native": "1.0.5", "sax": "1.2.4", "symbol-tree": "3.2.2", @@ -2239,7 +1906,7 @@ "webidl-conversions": "4.0.2", "whatwg-encoding": "1.0.3", "whatwg-mimetype": "2.1.0", - "whatwg-url": "6.4.1", + "whatwg-url": "6.5.0", "ws": "4.1.0", "xml-name-validator": "3.0.0" } @@ -2250,6 +1917,12 @@ "integrity": "sha1-a9KZwTsMRiay2iwDk81DhdYGrLk=", "dev": true }, + "jsesc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz", + "integrity": "sha1-5CGiqOINawgZ3yiQj3glJrlt0f4=", + "dev": true + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -2329,12 +2002,6 @@ "type-check": "0.3.2" } }, - "locate-character": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-2.0.5.tgz", - "integrity": "sha512-n2GmejDXtOPBAZdIiEFy5dJ5N38xBCXLNOtw2WpB9kGh6pnrEuKlwYI+Tkpofc4wDtVXHtoAOJaMRlYG/oYaxg==", - "dev": true - }, "lodash": { "version": "4.17.10", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", @@ -2359,6 +2026,15 @@ "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", "dev": true }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "dev": true, + "requires": { + "js-tokens": "3.0.2" + } + }, "magic-string": { "version": "0.22.5", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", @@ -2374,6 +2050,12 @@ "integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==", "dev": true }, + "math-random": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", + "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", + "dev": true + }, "micromatch": { "version": "2.3.11", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", @@ -2443,22 +2125,22 @@ } }, "mocha": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.1.1.tgz", - "integrity": "sha512-kKKs/H1KrMMQIEsWNxGmb4/BGsmj0dkeyotEvbrAuQ01FcWRLssUNXCEUZk6SZtyJBi6EE7SL0zDDtItw1rGhw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", "dev": true, "requires": { "browser-stdout": "1.3.1", - "commander": "2.11.0", + "commander": "2.15.1", "debug": "3.1.0", "diff": "3.5.0", "escape-string-regexp": "1.0.5", "glob": "7.1.2", - "growl": "1.10.3", + "growl": "1.10.5", "he": "1.1.1", "minimatch": "3.0.4", "mkdirp": "0.5.1", - "supports-color": "4.4.0" + "supports-color": "5.4.0" } }, "ms": { @@ -2498,16 +2180,16 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, - "nwmatcher": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.4.tgz", - "integrity": "sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ==", + "nwsapi": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.3.tgz", + "integrity": "sha512-zFJF9lOpg2+uicP0BQKOAfIOqeTp/p8PC669mewxgRkR1hGjne8BMUHk4wpRS9o5Z0icA5Nv04HmGkW31KfMKw==", "dev": true }, "nyc": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-11.7.1.tgz", - "integrity": "sha512-EGePURSKUEpS1jWnEKAMhY+GWZzi7JC+f8iBDOATaOsLZW5hM/9eYx2dHGaEXa1ITvMm44CJugMksvP3NwMQMw==", + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-12.0.2.tgz", + "integrity": "sha1-ikpO1pCWbBHsWH/4fuoMEsl0upk=", "dev": true, "requires": { "archy": "1.0.0", @@ -2522,13 +2204,13 @@ "glob": "7.1.2", "istanbul-lib-coverage": "1.2.0", "istanbul-lib-hook": "1.1.0", - "istanbul-lib-instrument": "1.10.1", + "istanbul-lib-instrument": "2.2.0", "istanbul-lib-report": "1.1.3", - "istanbul-lib-source-maps": "1.2.3", - "istanbul-reports": "1.4.0", + "istanbul-lib-source-maps": "1.2.5", + "istanbul-reports": "1.4.1", "md5-hex": "1.3.0", "merge-source-map": "1.1.0", - "micromatch": "2.3.11", + "micromatch": "3.1.10", "mkdirp": "0.5.1", "resolve-from": "2.0.0", "rimraf": "2.6.2", @@ -2557,15 +2239,9 @@ "dev": true }, "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "append-transform": { @@ -2584,18 +2260,15 @@ "dev": true }, "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "1.1.0" - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true }, "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", "dev": true }, "arr-union": { @@ -2605,9 +2278,9 @@ "dev": true }, "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, "arrify": { @@ -2629,103 +2302,9 @@ "dev": true }, "atob": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.0.tgz", - "integrity": "sha512-SuiKH8vbsOyCALjA/+EINmt/Kdl+TQPrtFgW7XZZcwtryFu9e5kQoX3bjCW6mIvGH1fbeAZZuvwGR5IlBRznGw==", - "dev": true - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" - } - }, - "babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "dev": true, - "requires": { - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "detect-indent": "4.0.0", - "jsesc": "1.3.0", - "lodash": "4.17.5", - "source-map": "0.5.7", - "trim-right": "1.0.1" - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "2.5.5", - "regenerator-runtime": "0.11.1" - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "lodash": "4.17.5" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "debug": "2.6.9", - "globals": "9.18.0", - "invariant": "2.2.4", - "lodash": "4.17.5" - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "esutils": "2.0.2", - "lodash": "4.17.5", - "to-fast-properties": "1.0.3" - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", + "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", "dev": true }, "balanced-match": { @@ -2737,7 +2316,7 @@ "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", "dev": true, "requires": { "cache-base": "1.0.1", @@ -2761,7 +2340,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "6.0.2" @@ -2770,7 +2349,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "6.0.2" @@ -2779,7 +2358,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "1.0.0", @@ -2787,16 +2366,10 @@ "kind-of": "6.0.2" } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true } } @@ -2804,7 +2377,7 @@ "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", "dev": true, "requires": { "balanced-match": "1.0.0", @@ -2812,14 +2385,32 @@ } }, "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", "dev": true, "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.2", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } } }, "builtin-modules": { @@ -2831,7 +2422,7 @@ "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", "dev": true, "requires": { "collection-visit": "1.0.0", @@ -2843,14 +2434,6 @@ "to-object-path": "0.3.0", "union-value": "1.0.0", "unset-value": "1.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, "caching-transform": { @@ -2882,23 +2465,10 @@ "lazy-cache": "1.0.4" } }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", "dev": true, "requires": { "arr-union": "3.1.0", @@ -2915,12 +2485,6 @@ "requires": { "is-descriptor": "0.1.6" } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true } } }, @@ -2991,26 +2555,20 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, - "core-js": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.5.tgz", - "integrity": "sha1-sU3ek2xkDAV5prUMq8wTLdYSfjs=", - "dev": true - }, "cross-spawn": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", "dev": true, "requires": { - "lru-cache": "4.1.2", - "which": "1.3.0" + "lru-cache": "4.1.3", + "which": "1.3.1" } }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -3046,7 +2604,7 @@ "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", "dev": true, "requires": { "is-descriptor": "1.0.2", @@ -3056,7 +2614,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "6.0.2" @@ -3065,7 +2623,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "6.0.2" @@ -3074,7 +2632,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "1.0.0", @@ -3082,29 +2640,14 @@ "kind-of": "6.0.2" } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true } } }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "requires": { - "repeating": "2.0.1" - } - }, "error-ex": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", @@ -3114,18 +2657,6 @@ "is-arrayish": "0.2.1" } }, - "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 - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, "execa": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", @@ -3147,29 +2678,55 @@ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "lru-cache": "4.1.2", + "lru-cache": "4.1.3", "shebang-command": "1.2.0", - "which": "1.3.0" + "which": "1.3.1" } } } }, "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "is-posix-bracket": "0.1.1" - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "2.2.3" + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } } }, "extend-shallow": { @@ -3185,7 +2742,7 @@ "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { "is-plain-object": "2.0.4" @@ -3194,31 +2751,97 @@ } }, "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", + "dev": true + } } }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "1.1.7", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } } }, "find-cache-dir": { @@ -3247,15 +2870,6 @@ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "requires": { - "for-in": "1.0.2" - } - }, "foreground-child": { "version": "1.5.6", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", @@ -3302,7 +2916,7 @@ "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", "dev": true, "requires": { "fs.realpath": "1.0.0", @@ -3313,31 +2927,6 @@ "path-is-absolute": "1.0.1" } }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "2.0.1" - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", @@ -3367,21 +2956,6 @@ } } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -3391,14 +2965,6 @@ "get-value": "2.0.6", "has-values": "1.0.0", "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, "has-values": { @@ -3411,26 +2977,6 @@ "kind-of": "4.0.0" }, "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", @@ -3445,7 +2991,7 @@ "hosted-git-info": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", - "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==", + "integrity": "sha1-IyNbKasjDFdqqw1PE/wEawsDgiI=", "dev": true }, "imurmurhash": { @@ -3470,15 +3016,6 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "1.3.1" - } - }, "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", @@ -3503,7 +3040,7 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", "dev": true }, "is-builtin-module": { @@ -3527,7 +3064,7 @@ "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", @@ -3538,66 +3075,27 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", "dev": true } } }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "requires": { - "is-primitive": "2.0.0" - } - }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { "kind-of": "3.2.2" @@ -3606,7 +3104,7 @@ "is-odd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", - "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", + "integrity": "sha1-dkZiRnH9fqVYzNmieVGC8pWPGyQ=", "dev": true, "requires": { "is-number": "4.0.0" @@ -3615,7 +3113,7 @@ "is-number": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "integrity": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", "dev": true } } @@ -3623,32 +3121,12 @@ "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", "dev": true, "requires": { "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -3664,7 +3142,7 @@ "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=", "dev": true }, "isarray": { @@ -3680,48 +3158,30 @@ "dev": true }, "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true }, "istanbul-lib-coverage": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz", - "integrity": "sha512-GvgM/uXRwm+gLlvkWHTjDAvwynZkL9ns15calTrmhGgowlwJBbWMYzWbKqE2DT6JDP1AFXKa+Zi0EkqNCUqY0A==", + "integrity": "sha1-99jy5CuX43/nlhFMsPnWi146Q0E=", "dev": true }, "istanbul-lib-hook": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz", - "integrity": "sha512-U3qEgwVDUerZ0bt8cfl3dSP3S6opBoOtk3ROO5f2EfBr/SRiD9FQqzwaZBqFORu8W7O0EXpai+k7kxHK13beRg==", + "integrity": "sha1-hTjZcDcss3FtU+VVI91UtVeo2Js=", "dev": true, "requires": { "append-transform": "0.4.0" } }, - "istanbul-lib-instrument": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz", - "integrity": "sha512-1dYuzkOCbuR5GRJqySuZdsmsNKPL3PTuyPevQfoCXJePT9C8y1ga75neU+Tuy9+yS3G/dgx8wgOmp2KLpgdoeQ==", - "dev": true, - "requires": { - "babel-generator": "6.26.1", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "istanbul-lib-coverage": "1.2.0", - "semver": "5.5.0" - } - }, "istanbul-lib-report": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.3.tgz", - "integrity": "sha512-D4jVbMDtT2dPmloPJS/rmeP626N5Pr3Rp+SovrPn1+zPChGHcggd/0sL29jnbm4oK9W0wHjCRsdch9oLd7cm6g==", + "integrity": "sha1-LfEhiMD6d5kMDSF20tC6M5QYglk=", "dev": true, "requires": { "istanbul-lib-coverage": "1.2.0", @@ -3730,6 +3190,12 @@ "supports-color": "3.2.3" }, "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, "supports-color": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", @@ -3742,9 +3208,9 @@ } }, "istanbul-lib-source-maps": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.3.tgz", - "integrity": "sha512-fDa0hwU/5sDXwAklXgAoCJCOsFsBplVQ6WBldz5UwaqOzmDhUK4nfuR7/G//G2lERlblUNJB8P6e8cXq3a7MlA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.5.tgz", + "integrity": "sha1-/+a+Tnq4bTYD5CkNVJkLFFBvybE=", "dev": true, "requires": { "debug": "3.1.0", @@ -3752,40 +3218,17 @@ "mkdirp": "0.5.1", "rimraf": "2.6.2", "source-map": "0.5.7" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } } }, "istanbul-reports": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.4.0.tgz", - "integrity": "sha512-OPzVo1fPZ2H+owr8q/LYKLD+vquv9Pj4F+dj808MdHbuQLD7S4ACRjcX+0Tne5Vxt2lxXvdZaL7v+FOOAV281w==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.4.1.tgz", + "integrity": "sha1-Ty6OkoqnoF0dpsQn1AmLJlXsczQ=", "dev": true, "requires": { "handlebars": "4.0.11" } }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -3842,31 +3285,16 @@ } } }, - "lodash": { - "version": "4.17.5", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", - "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", - "dev": true - }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", "dev": true }, - "loose-envify": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", - "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", - "dev": true, - "requires": { - "js-tokens": "3.0.2" - } - }, "lru-cache": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz", - "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha1-oRdc80lt/IQ2wVbDNLSVWZK85pw=", "dev": true, "requires": { "pseudomap": "1.0.2", @@ -3915,7 +3343,7 @@ "merge-source-map": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "integrity": "sha1-L93n5gIJOfcJBqaPLXrmheTIxkY=", "dev": true, "requires": { "source-map": "0.6.1" @@ -3924,42 +3352,50 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } }, "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.9", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", + "dev": true + } } }, "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==", + "integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI=", "dev": true }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "dev": true, "requires": { "brace-expansion": "1.1.11" @@ -3974,7 +3410,7 @@ "mixin-deep": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "integrity": "sha1-pJ5yaNzhoNlpjkUybFYm3zVD0P4=", "dev": true, "requires": { "for-in": "1.0.2", @@ -3984,7 +3420,7 @@ "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { "is-plain-object": "2.0.4" @@ -4010,7 +3446,7 @@ "nanomatch": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", - "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", + "integrity": "sha1-h59xUMstq3pHElkGbBBO7m4Pp8I=", "dev": true, "requires": { "arr-diff": "4.0.0", @@ -4027,22 +3463,10 @@ "to-regex": "3.0.2" }, "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true } } @@ -4050,7 +3474,7 @@ "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=", "dev": true, "requires": { "hosted-git-info": "2.6.0", @@ -4059,15 +3483,6 @@ "validate-npm-package-license": "3.0.3" } }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "1.1.0" - } - }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -4118,24 +3533,6 @@ "dev": true, "requires": { "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" } }, "object.pick": { @@ -4145,14 +3542,6 @@ "dev": true, "requires": { "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, "once": { @@ -4183,7 +3572,7 @@ "os-locale": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "integrity": "sha1-QrwpAKa1uL0XN2yOiCtlr8zyS/I=", "dev": true, "requires": { "execa": "0.7.0", @@ -4200,7 +3589,7 @@ "p-limit": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", - "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", + "integrity": "sha1-DpK2vty1nwIsE9DxlJ3ILRWQnxw=", "dev": true, "requires": { "p-try": "1.0.0" @@ -4221,18 +3610,6 @@ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" - } - }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", @@ -4334,59 +3711,12 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, - "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", - "dev": true, - "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -4420,37 +3750,16 @@ } } }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "requires": { - "is-equal-shallow": "0.1.3" - } - }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", "dev": true, "requires": { "extend-shallow": "3.0.2", "safe-regex": "1.1.0" } }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, "repeat-element": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", @@ -4463,15 +3772,6 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "1.0.2" - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -4499,7 +3799,7 @@ "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=", "dev": true }, "right-align": { @@ -4515,7 +3815,7 @@ "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", "dev": true, "requires": { "glob": "7.1.2" @@ -4533,7 +3833,7 @@ "semver": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "integrity": "sha1-3Eu8emyp2Rbe5dQ1FvAJK1j3uKs=", "dev": true }, "set-blocking": { @@ -4545,7 +3845,7 @@ "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=", "dev": true, "requires": { "extend-shallow": "2.0.1", @@ -4595,7 +3895,7 @@ "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", "dev": true, "requires": { "base": "0.11.2", @@ -4604,10 +3904,19 @@ "extend-shallow": "2.0.1", "map-cache": "0.2.2", "source-map": "0.5.7", - "source-map-resolve": "0.5.1", + "source-map-resolve": "0.5.2", "use": "3.1.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -4631,7 +3940,7 @@ "snapdragon-node": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", "dev": true, "requires": { "define-property": "1.0.0", @@ -4651,7 +3960,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "6.0.2" @@ -4660,7 +3969,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "6.0.2" @@ -4669,7 +3978,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "1.0.0", @@ -4677,16 +3986,10 @@ "kind-of": "6.0.2" } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true } } @@ -4694,7 +3997,7 @@ "snapdragon-util": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", "dev": true, "requires": { "kind-of": "3.2.2" @@ -4707,12 +4010,12 @@ "dev": true }, "source-map-resolve": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", - "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha1-cuLMNAlVQ+Q7LGKyxMENSpBU8lk=", "dev": true, "requires": { - "atob": "2.1.0", + "atob": "2.1.1", "decode-uri-component": "0.2.0", "resolve-url": "0.2.1", "source-map-url": "0.4.0", @@ -4728,7 +4031,7 @@ "spawn-wrap": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.2.tgz", - "integrity": "sha512-vMwR3OmmDhnxCVxM8M+xO/FtIp6Ju/mNaDfCMMW7FDcLRTPFWUswec4LXJHTJE2hwTI9O0YBfygu4DalFl7Ylg==", + "integrity": "sha1-z/WOc6giRhe2Vhq9wyWG6gyCJIw=", "dev": true, "requires": { "foreground-child": "1.5.6", @@ -4736,13 +4039,13 @@ "os-homedir": "1.0.2", "rimraf": "2.6.2", "signal-exit": "3.0.2", - "which": "1.3.0" + "which": "1.3.1" } }, "spdx-correct": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", - "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", + "integrity": "sha1-BaW01xU6GVvJLDxCW2nzsqlSTII=", "dev": true, "requires": { "spdx-expression-parse": "3.0.0", @@ -4752,13 +4055,13 @@ "spdx-exceptions": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", + "integrity": "sha1-LHrmEFbHFKW5ubKyr30xHvXHj+k=", "dev": true }, "spdx-expression-parse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "integrity": "sha1-meEZt6XaAOBUkcn6M4t5BII7QdA=", "dev": true, "requires": { "spdx-exceptions": "2.1.0", @@ -4768,13 +4071,13 @@ "spdx-license-ids": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", - "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", + "integrity": "sha1-enzShHDMbToc/m1miG9rxDDTrIc=", "dev": true }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", "dev": true, "requires": { "extend-shallow": "3.0.2" @@ -4804,37 +4107,20 @@ "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", "dev": true, "requires": { "is-fullwidth-code-point": "2.0.0", "strip-ansi": "4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "3.0.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": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "3.0.0" } }, "strip-bom": { @@ -4852,16 +4138,10 @@ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, "test-exclude": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.2.1.tgz", - "integrity": "sha512-qpqlP/8Zl+sosLxBcVKl9vYy26T9NPalxSzzCP/OY6K7j938ui2oKgo+kRZYfxAeIpLqpbVnsHq1tyV70E4lWQ==", + "integrity": "sha1-36Ii8DSAvKaSB8pyizfXS0X3JPo=", "dev": true, "requires": { "arrify": "1.0.1", @@ -4869,290 +4149,8 @@ "object-assign": "4.1.1", "read-pkg-up": "1.0.1", "require-main-filename": "1.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "repeat-element": "1.1.2", - "snapdragon": "0.8.2", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.2" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "1.0.2" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.9", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" - } - } } }, - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -5165,7 +4163,7 @@ "to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", "dev": true, "requires": { "define-property": "2.0.2", @@ -5182,25 +4180,8 @@ "requires": { "is-number": "3.0.0", "repeat-string": "1.6.1" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - } } }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, "uglify-js": { "version": "2.8.29", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", @@ -5307,12 +4288,6 @@ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true } } }, @@ -5325,7 +4300,7 @@ "use": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", - "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", + "integrity": "sha1-FHFr8D/f79AwQK71jYtLhfOnxUQ=", "dev": true, "requires": { "kind-of": "6.0.2" @@ -5334,7 +4309,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true } } @@ -5342,7 +4317,7 @@ "validate-npm-package-license": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", - "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", + "integrity": "sha1-gWQ7y+8b3+zUYjeT3EZIlIupgzg=", "dev": true, "requires": { "spdx-correct": "3.0.0", @@ -5350,9 +4325,9 @@ } }, "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", "dev": true, "requires": { "isexe": "2.0.0" @@ -5387,6 +4362,12 @@ "strip-ansi": "3.0.1" }, "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "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", @@ -5406,6 +4387,15 @@ "is-fullwidth-code-point": "1.0.0", "strip-ansi": "3.0.1" } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } } } }, @@ -5441,10 +4431,10 @@ "yargs": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", - "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", + "integrity": "sha1-kLhpk07W6HERXqL/WLA/RyTtLXc=", "dev": true, "requires": { - "cliui": "4.0.0", + "cliui": "4.1.0", "decamelize": "1.2.0", "find-up": "2.1.0", "get-caller-file": "1.0.2", @@ -5458,12 +4448,6 @@ "yargs-parser": "9.0.2" }, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, "camelcase": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", @@ -5471,9 +4455,9 @@ "dev": true }, "cliui": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.0.0.tgz", - "integrity": "sha512-nY3W5Gu2racvdDk//ELReY+dHjb9PlIcVDFXP72nVIhq2Gy3LuVXYwJoPVudwQnv1shtohpgkdCKT2YaKY0CKw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha1-NIQi2+gtgAswIu709qwQvy5NG0k=", "dev": true, "requires": { "string-width": "2.1.1", @@ -5481,15 +4465,6 @@ "wrap-ansi": "2.1.0" } }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - }, "yargs-parser": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", @@ -5504,7 +4479,7 @@ "yargs-parser": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", - "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", + "integrity": "sha1-8TdqM7Ziml0GN4KUTacyYx6WaVA=", "dev": true, "requires": { "camelcase": "4.1.0" @@ -5586,12 +4561,6 @@ "is-glob": "2.0.1" } }, - "parse-ms": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz", - "integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0=", - "dev": true - }, "parse5": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", @@ -5622,15 +4591,6 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, - "plur": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/plur/-/plur-2.1.2.tgz", - "integrity": "sha1-dIJFLBoPUI4+NE6uwxLJHCncZVo=", - "dev": true, - "requires": { - "irregular-plurals": "1.4.0" - } - }, "pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", @@ -5649,16 +4609,6 @@ "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", "dev": true }, - "pretty-ms": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-3.1.0.tgz", - "integrity": "sha1-6crJx2v27lL+lC3ZxsQhMVOxKIE=", - "dev": true, - "requires": { - "parse-ms": "1.0.1", - "plur": "2.1.2" - } - }, "promise-polyfill": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-7.1.2.tgz", @@ -5666,9 +4616,9 @@ "dev": true }, "protobufjs": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.2.tgz", - "integrity": "sha1-WXSNfc8D0tsiwT2p/rAk4Wq4DJE=", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.3.tgz", + "integrity": "sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA==", "dev": true, "requires": { "ascli": "1.0.1", @@ -5690,43 +4640,27 @@ "dev": true }, "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz", + "integrity": "sha512-VdxFOIEY3mNO5PtSRkkle/hPJDHvQhK21oa73K4yAc9qmp6N429gAyF1gZMOTMeS0/AYzaV/2Trcef+NaIonSA==", "dev": true, "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" + "is-number": "4.0.0", + "kind-of": "6.0.2", + "math-random": "1.0.1" }, "dependencies": { "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true }, "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true } } }, @@ -5758,9 +4692,9 @@ "dev": true }, "request": { - "version": "2.85.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz", - "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==", + "version": "2.87.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", + "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", "dev": true, "requires": { "aws-sign2": "0.7.0", @@ -5771,7 +4705,6 @@ "forever-agent": "0.6.1", "form-data": "2.3.2", "har-validator": "5.0.3", - "hawk": "6.0.2", "http-signature": "1.2.0", "is-typedarray": "1.0.0", "isstream": "0.1.2", @@ -5781,7 +4714,6 @@ "performance-now": "2.1.0", "qs": "6.5.2", "safe-buffer": "5.1.2", - "stringstream": "0.0.5", "tough-cookie": "2.3.4", "tunnel-agent": "0.6.0", "uuid": "3.2.1" @@ -5807,12 +4739,6 @@ "tough-cookie": "2.3.4" } }, - "require-relative": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", - "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", - "dev": true - }, "resolve": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", @@ -5823,42 +4749,25 @@ } }, "rollup": { - "version": "0.57.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.57.1.tgz", - "integrity": "sha512-I18GBqP0qJoJC1K1osYjreqA8VAKovxuI3I81RSk0Dmr4TgloI0tAULjZaox8OsJ+n7XRrhH6i0G2By/pj1LCA==", - "dev": true, - "requires": { - "@types/acorn": "4.0.3", - "acorn": "5.5.3", - "acorn-dynamic-import": "3.0.0", - "date-time": "2.1.0", - "is-reference": "1.1.0", - "locate-character": "2.0.5", - "pretty-ms": "3.1.0", - "require-relative": "0.8.7", - "rollup-pluginutils": "2.0.1", - "signal-exit": "3.0.2", - "sourcemap-codec": "1.4.1" + "version": "0.60.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.60.4.tgz", + "integrity": "sha512-55x0sY6LPF7waMOaDa6QvTfEngyYpMNh48UWNEiZzwGA8x+sP2aH4c+H+1fTpFLay8Q6w1eE5Asrty7NW0nfrQ==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "@types/node": "10.3.2" } }, "rollup-plugin-commonjs": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.1.0.tgz", - "integrity": "sha512-NrfE0g30QljNCnlJr7I2Xguz+44mh0dCxvfxwLnCwtaCK2LwFUp1zzAs8MQuOfhH4mRskqsjfOwGUap/L+WtEw==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.1.3.tgz", + "integrity": "sha512-g91ZZKZwTW7F7vL6jMee38I8coj/Q9GBdTmXXeFL7ldgC1Ky5WJvHgbKlAiXXTh762qvohhExwUgeQGFh9suGg==", "dev": true, "requires": { "estree-walker": "0.5.2", "magic-string": "0.22.5", "resolve": "1.7.1", - "rollup-pluginutils": "2.0.1" - }, - "dependencies": { - "estree-walker": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.5.2.tgz", - "integrity": "sha512-XpCnW/AE10ws/kDAs37cngSkvgIR8aN3G0MS85m7dUpuK2EREo9VJ00uvw6Dg/hXEpfsE1I1TvJOJr+Z+TL+ig==", - "dev": true - } + "rollup-pluginutils": "2.3.0" } }, "rollup-plugin-node-resolve": { @@ -5873,51 +4782,42 @@ } }, "rollup-plugin-typescript2": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.12.0.tgz", - "integrity": "sha512-UvHzfJyBWyV+d4Av7FG79PTipIK1VrNQoDrNWszAYd7i/ZcCfBg3rPkPiK9Z+WEHXeWBbCnxJlSqR2zKWgEkiw==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.15.0.tgz", + "integrity": "sha512-0SiFfXhs41wQ94RIzw8EDM9vblyLNFNDgyqX1rci6e1KpYcxIbNH67nzv0DCS31LnA7K4F9irTl+ZPezvm/zug==", "dev": true, "requires": { "fs-extra": "5.0.0", "resolve": "1.7.1", - "rollup-pluginutils": "2.0.1", - "tslib": "1.9.0" + "rollup-pluginutils": "2.3.0", + "tslib": "1.9.2" + }, + "dependencies": { + "tslib": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.2.tgz", + "integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw==", + "dev": true + } } }, "rollup-plugin-uglify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-uglify/-/rollup-plugin-uglify-3.0.0.tgz", - "integrity": "sha512-dehLu9eRRoV4l09aC+ySntRw1OAfoyKdbk8Nelblj03tHoynkSybqyEpgavemi1LBOH6S1vzI58/mpxkZIe1iQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-uglify/-/rollup-plugin-uglify-4.0.0.tgz", + "integrity": "sha512-f6W31EQLzxSEYfN3x6/lyljHqXSoCjXKcTsnwz3evQvHgU1+qTzU2SE0SIG7tbAvaCewp2UaZ5x3k6nYsxOP9A==", "dev": true, "requires": { - "uglify-es": "3.3.9" - }, - "dependencies": { - "commander": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", - "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", - "dev": true - }, - "uglify-es": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", - "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", - "dev": true, - "requires": { - "commander": "2.13.0", - "source-map": "0.6.1" - } - } + "@babel/code-frame": "7.0.0-beta.49", + "uglify-js": "3.4.0" } }, "rollup-pluginutils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.0.1.tgz", - "integrity": "sha1-fslbNXP2VDpGpkYb2afFRFJdD8A=", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.3.0.tgz", + "integrity": "sha512-xB6hsRsjdJdIYWEyYUJy/3ki5g69wrf0luHPGNK3ZSocV6HLNfio59l3dZ3TL4xUwEKgROhFi9jOCt6c5gfUWw==", "dev": true, "requires": { - "estree-walker": "0.3.1", + "estree-walker": "0.5.2", "micromatch": "2.3.11" } }, @@ -5945,21 +4845,6 @@ "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "sntp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", - "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", - "dev": true, - "requires": { - "hoek": "4.2.1" - } - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -5967,21 +4852,15 @@ "dev": true }, "source-map-support": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.5.tgz", - "integrity": "sha512-mR7/Nd5l1z6g99010shcXJiNEaf3fEtmLhRB/sBcQVJGodcHCULPp2y4Sfa43Kv2zq7T+Izmfp/WHCR6dYkQCA==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz", + "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==", "dev": true, "requires": { - "buffer-from": "1.0.0", + "buffer-from": "1.1.0", "source-map": "0.6.1" } }, - "sourcemap-codec": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.1.tgz", - "integrity": "sha512-hX1eNBNuilj8yfFnECh0DzLgwKpBLMIvmhgEhixXNui8lMLBInTI8Kyxt++RwJnMNu7cAUo635L2+N1TxMJCzA==", - "dev": true - }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -5989,9 +4868,9 @@ "dev": true }, "sshpk": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", - "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", "dev": true, "requires": { "asn1": "0.2.3", @@ -6001,6 +4880,7 @@ "ecc-jsbn": "0.1.1", "getpass": "0.1.7", "jsbn": "0.1.1", + "safer-buffer": "2.1.2", "tweetnacl": "0.14.5" } }, @@ -6021,12 +4901,6 @@ "strip-ansi": "3.0.1" } }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", - "dev": true - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -6037,12 +4911,12 @@ } }, "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "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": "2.0.0" + "has-flag": "3.0.0" } }, "symbol-tree": { @@ -6051,10 +4925,10 @@ "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", "dev": true }, - "time-zone": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", - "integrity": "sha1-mcW/VZWJZq9tBtg73zgA3IL67F0=", + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, "tough-cookie": { @@ -6072,30 +4946,35 @@ "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", "dev": true, "requires": { - "punycode": "2.1.0" + "punycode": "2.1.1" }, "dependencies": { "punycode": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true } } }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, "ts-node": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-5.0.1.tgz", - "integrity": "sha512-XK7QmDcNHVmZkVtkiwNDWiERRHPyU8nBqZB1+iv2UhOG0q3RQ9HsZ2CMqISlFbxjrYFGfG2mX7bW4dAyxBVzUw==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-6.1.1.tgz", + "integrity": "sha512-79FnymLGDBd/nXoiak1L6w6fd9Zz9Ge/x8/Aglaeh31KkqRLDzbfT1vBGlO5dqc76WzufTlW4IYl7e01CVUF5A==", "dev": true, "requires": { "arrify": "1.0.1", - "chalk": "2.4.1", "diff": "3.5.0", "make-error": "1.3.4", "minimist": "1.2.0", "mkdirp": "0.5.1", - "source-map-support": "0.5.5", + "source-map-support": "0.5.6", "yn": "2.0.0" } }, @@ -6117,12 +4996,12 @@ "commander": "2.15.1", "diff": "3.5.0", "glob": "7.1.2", - "js-yaml": "3.11.0", + "js-yaml": "3.12.0", "minimatch": "3.0.4", "resolve": "1.7.1", "semver": "5.5.0", "tslib": "1.9.0", - "tsutils": "2.26.2" + "tsutils": "2.27.1" }, "dependencies": { "builtin-modules": { @@ -6130,19 +5009,13 @@ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "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 } } }, "tsutils": { - "version": "2.26.2", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.26.2.tgz", - "integrity": "sha512-uzwnhmrSbyinPCiwfzGsOY3IulBTwoky7r83HmZdz9QNCjhSCzavkh47KLWuU0zF2F2WbpmmzoJUIEiYyd+jEQ==", + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.27.1.tgz", + "integrity": "sha512-AE/7uzp32MmaHvNNFES85hhUDHFdFZp6OAiZcd6y4ZKKIg6orJTm8keYWBhIhrJQH3a4LzNKat7ZPXZt5aTf6w==", "dev": true, "requires": { "tslib": "1.9.0" @@ -6180,11 +5053,21 @@ "dev": true }, "typescript": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.8.3.tgz", - "integrity": "sha512-K7g15Bb6Ra4lKf7Iq2l/I5/En+hLIHmxWZGq3D4DIRNFxMNV6j2SHSvDOqs2tGd4UvD/fJvrwopzQXjLrT7Itw==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.1.tgz", + "integrity": "sha512-h6pM2f/GDchCFlldnriOhs1QHuwbnmj6/v7499eMHqPeW4V2G0elua2eIc2nu8v2NdHV0Gm+tzX83Hr6nUFjQA==", "dev": true }, + "uglify-js": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.0.tgz", + "integrity": "sha512-Jcf5naPkX3rVPSQpRn9Vm6Rr572I1gTtR9LnqKgXjmOgfYQ/QS0V2WRStFR53Bdj520M66aCZqt9uzYXgtGrJQ==", + "dev": true, + "requires": { + "commander": "2.15.1", + "source-map": "0.6.1" + } + }, "universalify": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", @@ -6235,7 +5118,7 @@ "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", "dev": true, "requires": { - "http-parser-js": "0.4.12", + "http-parser-js": "0.4.13", "websocket-extensions": "0.1.3" } }, @@ -6275,9 +5158,9 @@ "dev": true }, "whatwg-url": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.4.1.tgz", - "integrity": "sha512-FwygsxsXx27x6XXuExA/ox3Ktwcbf+OAvrKmLulotDAiO1Q6ixchPFaHYsis2zZBZSJTR0+dR+JVtf7MlbqZjw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", "dev": true, "requires": { "lodash.sortby": "4.7.0", diff --git a/package.json b/package.json index 19e2a354..90b88d15 100644 --- a/package.json +++ b/package.json @@ -29,28 +29,28 @@ "package.json" ], "peerDependencies": { - "firebase": "^2.4.0 || 3.x.x || 4.x.x" + "firebase": "^2.4.0 || 3.x.x || 4.x.x || 5.x.x" }, "devDependencies": { - "@types/chai": "^4.1.2", - "@types/mocha": "^2.2.48", - "@types/node": "^9.4.6", + "@types/chai": "^4.1.3", + "@types/mocha": "^5.2.2", + "@types/node": "^10.3.2", "chai": "^4.1.2", - "coveralls": "^3.0.0", - "firebase": "4.x.x", - "jsdom": "^11.6.2", + "coveralls": "^3.0.1", + "firebase": "5.x.x", + "jsdom": "^11.11.0", "jsdom-global": "^3.0.2", - "mocha": "^5.0.4", - "nyc": "^11.4.1", - "rollup": "0.57.1", - "rollup-plugin-commonjs": "9.1.0", - "rollup-plugin-node-resolve": "3.3.0", - "rollup-plugin-typescript2": "0.12.0", - "rollup-plugin-uglify": "3.0.0", - "source-map-support": "^0.5.3", - "ts-node": "^5.0.1", - "tslint": "^5.9.1", - "typescript": "^2.7.2" + "mocha": "^5.2.0", + "nyc": "^12.0.2", + "rollup": "^0.60.4", + "rollup-plugin-commonjs": "^9.1.3", + "rollup-plugin-node-resolve": "^3.3.0", + "rollup-plugin-typescript2": "^0.15.0", + "rollup-plugin-uglify": "^4.0.0", + "source-map-support": "^0.5.6", + "ts-node": "^6.1.1", + "tslint": "^5.10.0", + "typescript": "^2.9.1" }, "scripts": { "build": "rollup -c", diff --git a/rollup.config.js b/rollup.config.js index ba12d2ed..d354ca38 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,7 +1,7 @@ import commonjs from 'rollup-plugin-commonjs'; import resolveModule from 'rollup-plugin-node-resolve'; import typescript from 'rollup-plugin-typescript2'; -import uglify from 'rollup-plugin-uglify'; +import { uglify } from 'rollup-plugin-uglify'; import pkg from './package.json'; const GLOBAL_NAME = 'geofire'; From 08eda8ff705769e1fa54d031da1d043729a01593 Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Sat, 23 Jun 2018 14:33:22 -0400 Subject: [PATCH 22/23] refactor(GeoQuery): revert to using dict instead of a Map --- src/geoFire/geoQuery.ts | 64 ++++++++++----------- src/geoFire/interfaces/geoQueryCallbacks.ts | 11 ++-- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/geoFire/geoQuery.ts b/src/geoFire/geoQuery.ts index b9d1f795..f37add18 100644 --- a/src/geoFire/geoQuery.ts +++ b/src/geoFire/geoQuery.ts @@ -15,10 +15,10 @@ export class GeoQuery { private _cancelled: boolean = false; private _center: number[]; // A dictionary of geohash queries which currently have an active callbacks - private _currentGeohashesQueried: Map = new Map(); + private _currentGeohashesQueried: { [name: string]: GeoQueryState } = {}; // A dictionary of locations that a currently active in the queries // Note that not all of these are currently within this query - private _locationsTracked: Map = new Map(); + private _locationsTracked: { [name: string]: LocationTracked } = {}; private _radius: number; // Variables used to keep track of when to fire the 'ready' event @@ -72,15 +72,15 @@ export class GeoQuery { this._callbacks = { ready: [], key_entered: [], key_exited: [], key_moved: [] }; // Turn off all Firebase listeners for the current geohashes being queried - const keys: string[] = Array.from(this._currentGeohashesQueried.keys()); + const keys: string[] = Object.keys(this._currentGeohashesQueried); keys.forEach((geohashQueryStr: string) => { const query: string[] = this._stringToQuery(geohashQueryStr); - this._cancelGeohashQuery(query, this._currentGeohashesQueried.get(geohashQueryStr)); - this._currentGeohashesQueried.delete(geohashQueryStr); + this._cancelGeohashQuery(query, this._currentGeohashesQueried[geohashQueryStr]); + delete this._currentGeohashesQueried[geohashQueryStr]; }); // Delete any stored locations - this._locationsTracked = new Map(); + this._locationsTracked = {}; // Turn off the current geohashes queried clean up interval clearInterval(this._cleanUpCurrentGeohashesQueriedInterval); @@ -138,9 +138,9 @@ export class GeoQuery { // If this is a 'key_entered' callback, fire it for every location already within this query if (eventType === 'key_entered') { - const keys: string[] = Array.from(this._locationsTracked.keys()); + const keys: string[] = Object.keys(this._locationsTracked); keys.forEach((key: string) => { - const locationDict = this._locationsTracked.get(key); + const locationDict = this._locationsTracked[key]; if (typeof locationDict !== 'undefined' && locationDict.isInQuery) { callback(key, locationDict.location, locationDict.distanceFromCenter); } @@ -180,7 +180,7 @@ export class GeoQuery { // Loop through all of the locations in the query, update their distance from the center of the // query, and fire any appropriate events - const keys: string[] = Array.from(this._locationsTracked.keys()); + const keys: string[] = Object.keys(this._locationsTracked); for (const key of keys) { // If the query was cancelled while going through this loop, stop updating locations and stop // firing events @@ -188,7 +188,7 @@ export class GeoQuery { break; } // Get the cached information for this location - const locationDict = this._locationsTracked.get(key); + const locationDict = this._locationsTracked[key]; // Save if the location was already in the query const wasAlreadyInQuery = locationDict.isInQuery; // Update the location's distance to the new query center @@ -254,7 +254,7 @@ export class GeoQuery { */ private _childRemovedCallback(locationDataSnapshot: firebase.database.DataSnapshot): void { const key: string = geoFireGetKey(locationDataSnapshot); - if (this._locationsTracked.has(key)) { + if (key in this._locationsTracked) { this._firebaseRef.child(key).once('value', (snapshot: firebase.database.DataSnapshot) => { const location: number[] = (snapshot.val() === null) ? null : decodeGeoFireObject(snapshot.val()); const geohash: string = (location !== null) ? encodeGeohash(location) : null; @@ -272,25 +272,25 @@ export class GeoQuery { * Removes unnecessary Firebase queries which are currently being queried. */ private _cleanUpCurrentGeohashesQueried(): void { - let keys: string[] = Array.from(this._currentGeohashesQueried.keys()); + let keys: string[] = Object.keys(this._currentGeohashesQueried); keys.forEach((geohashQueryStr: string) => { - const queryState: any = this._currentGeohashesQueried.get(geohashQueryStr); + const queryState: any = this._currentGeohashesQueried[geohashQueryStr]; if (queryState.active === false) { const query = this._stringToQuery(geohashQueryStr); // Delete the geohash since it should no longer be queried this._cancelGeohashQuery(query, queryState); - this._currentGeohashesQueried.delete(geohashQueryStr); + delete this._currentGeohashesQueried[geohashQueryStr]; } }); // Delete each location which should no longer be queried - keys = Array.from(this._locationsTracked.keys()); + keys = Object.keys(this._locationsTracked); keys.forEach((key: string) => { - if (!this._geohashInSomeQuery(this._locationsTracked.get(key).geohash)) { - if (this._locationsTracked.get(key).isInQuery) { + if (!this._geohashInSomeQuery(this._locationsTracked[key].geohash)) { + if (this._locationsTracked[key].isInQuery) { throw new Error('Internal State error, trying to remove location that is still in query'); } - this._locationsTracked.delete(key); + delete this._locationsTracked[key]; } }); @@ -338,9 +338,9 @@ export class GeoQuery { * @returns Returns true if the geohash is part of any of the current geohash queries. */ private _geohashInSomeQuery(geohash: string): boolean { - const keys: string[] = Array.from(this._currentGeohashesQueried.keys()); + const keys: string[] = Object.keys(this._currentGeohashesQueried); for (const queryStr of keys) { - if (this._currentGeohashesQueried.has(queryStr)) { + if (queryStr in this._currentGeohashesQueried) { const query = this._stringToQuery(queryStr); if (geohash >= query[0] && geohash <= query[1]) { return true; @@ -382,20 +382,20 @@ export class GeoQuery { // For all of the geohashes that we are already currently querying, check if they are still // supposed to be queried. If so, don't re-query them. Otherwise, mark them to be un-queried // next time we clean up the current geohashes queried dictionary. - const keys: string[] = Array.from(this._currentGeohashesQueried.keys()); + const keys: string[] = Object.keys(this._currentGeohashesQueried); keys.forEach((geohashQueryStr: string) => { const index: number = geohashesToQuery.indexOf(geohashQueryStr); if (index === -1) { - this._currentGeohashesQueried.get(geohashQueryStr).active = false; + this._currentGeohashesQueried[geohashQueryStr].active = false; } else { - this._currentGeohashesQueried.get(geohashQueryStr).active = true; + this._currentGeohashesQueried[geohashQueryStr].active = true; geohashesToQuery.splice(index, 1); } }); // If we are not already cleaning up the current geohashes queried and we have more than 25 of them, // kick off a timeout to clean them up so we don't create an infinite number of unneeded queries. - if (this._geohashCleanupScheduled === false && this._currentGeohashesQueried.size > 25) { + if (this._geohashCleanupScheduled === false && Object.keys(this._currentGeohashesQueried).length > 25) { this._geohashCleanupScheduled = true; this._cleanUpCurrentGeohashesQueriedTimeout = setTimeout(this._cleanUpCurrentGeohashesQueried, 10); } @@ -427,13 +427,13 @@ export class GeoQuery { }); // Add the geohash query to the current geohashes queried dictionary and save its state - this._currentGeohashesQueried.set(toQueryStr, { + this._currentGeohashesQueried[toQueryStr] = { active: true, childAddedCallback, childRemovedCallback, childChangedCallback, valueCallback - }); + }; }); // Based upon the algorithm to calculate geohashes, it's possible that no 'new' // geohashes were queried even if the client updates the radius of the query. @@ -464,8 +464,8 @@ export class GeoQuery { * @param currentLocation The current location as [latitude, longitude] pair or null if removed. */ private _removeLocation(key: string, currentLocation?: number[]): void { - const locationDict = this._locationsTracked.get(key); - this._locationsTracked.delete(key); + const locationDict = this._locationsTracked[key]; + delete this._locationsTracked[key]; if (typeof locationDict !== 'undefined' && locationDict.isInQuery) { const distanceFromCenter: number = (currentLocation) ? distance(currentLocation, this._center) : null; this._fireCallbacksForKey('key_exited', key, currentLocation, distanceFromCenter); @@ -500,20 +500,20 @@ export class GeoQuery { validateLocation(location); // Get the key and location let distanceFromCenter: number, isInQuery; - const wasInQuery: boolean = (this._locationsTracked.has(key)) ? this._locationsTracked.get(key).isInQuery : false; - const oldLocation: number[] = (this._locationsTracked.has(key)) ? this._locationsTracked.get(key).location : null; + const wasInQuery: boolean = (key in this._locationsTracked) ? this._locationsTracked[key].isInQuery : false; + const oldLocation: number[] = (key in this._locationsTracked) ? this._locationsTracked[key].location : null; // Determine if the location is within this query distanceFromCenter = distance(location, this._center); isInQuery = (distanceFromCenter <= this._radius); // Add this location to the locations queried dictionary even if it is not within this query - this._locationsTracked.set(key, { + this._locationsTracked[key] = { location, distanceFromCenter, isInQuery, geohash: encodeGeohash(location) - }); + }; // Fire the 'key_entered' event if the provided key has entered this query if (isInQuery && !wasInQuery) { diff --git a/src/geoFire/interfaces/geoQueryCallbacks.ts b/src/geoFire/interfaces/geoQueryCallbacks.ts index c41ab645..6b2d22b2 100644 --- a/src/geoFire/interfaces/geoQueryCallbacks.ts +++ b/src/geoFire/interfaces/geoQueryCallbacks.ts @@ -1,6 +1,9 @@ +export type ReadyCallback = () => void; +export type KeyCallback = (key?: string, location?: number[], distanceFromCenter?: number) => void; + export interface GeoQueryCallbacks { - ready: any[]; - key_entered: any[]; - key_exited: any[]; - key_moved: any[]; + ready: ReadyCallback[]; + key_entered: KeyCallback[]; + key_exited: KeyCallback[]; + key_moved: KeyCallback[]; } \ No newline at end of file From 7830059dd705e8f37b8082fdbbbcd1d13e1c04a6 Mon Sep 17 00:00:00 2001 From: Michael Solati Date: Thu, 28 Jun 2018 09:50:08 -0700 Subject: [PATCH 23/23] refactor(GeoQueryState): create interface for callbacks --- src/geoFire/interfaces/geoQueryState.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/geoFire/interfaces/geoQueryState.ts b/src/geoFire/interfaces/geoQueryState.ts index fee89011..7abfde13 100644 --- a/src/geoFire/interfaces/geoQueryState.ts +++ b/src/geoFire/interfaces/geoQueryState.ts @@ -1,9 +1,11 @@ import * as firebase from 'firebase'; +export type GeoQueryStateCallback = (a: firebase.database.DataSnapshot | null, b?: string) => any + export interface GeoQueryState { active: boolean; - childAddedCallback: (a: firebase.database.DataSnapshot, b?: string) => any; - childRemovedCallback: (a: firebase.database.DataSnapshot, b?: string) => any; - childChangedCallback: (a: firebase.database.DataSnapshot, b?: string) => any; - valueCallback: (a: firebase.database.DataSnapshot, b?: string) => any; + childAddedCallback: GeoQueryStateCallback; + childRemovedCallback: GeoQueryStateCallback; + childChangedCallback: GeoQueryStateCallback; + valueCallback: GeoQueryStateCallback; } \ No newline at end of file