diff --git a/package-lock.json b/package-lock.json index 2c8190a..9c3a55b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -832,6 +832,51 @@ "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", "dev": true }, + "@sinonjs/commons": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@sinonjs/formatio": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz", + "integrity": "sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1", + "@sinonjs/samsam": "^5.0.2" + } + }, + "@sinonjs/samsam": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.0.tgz", + "integrity": "sha512-hXpcfx3aq+ETVBwPlRFICld5EnrkexXuXDwqUNhDdr5L8VjvMeSRwyOa0qL7XFmR+jVWR4rUZtnxlG7RX72sBg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "dev": true + }, "@szmarczak/http-timer": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", @@ -882,6 +927,12 @@ "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", "dev": true }, + "@types/node": { + "version": "14.14.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.12.tgz", + "integrity": "sha512-ASH8OPHMNlkdjrEdmoILmzFfsJICvhBsFfAum4aKZ/9U4B6M6tTmTPh+f3ttWdD74CEGV5XvXWkbyfSdXaTd7g==", + "dev": true + }, "@types/normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", @@ -894,6 +945,21 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "@types/sinon": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.9.tgz", + "integrity": "sha512-z/y8maYOQyYLyqaOB+dYQ6i0pxKLOsfwCmHmn4T7jS/SDHicIslr37oE3Dg8SCqKrKeBy6Lemu7do2yy+unLrw==", + "dev": true, + "requires": { + "@types/sinonjs__fake-timers": "*" + } + }, + "@types/sinonjs__fake-timers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz", + "integrity": "sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.9.0.tgz", @@ -987,12 +1053,42 @@ "through": ">=2.2.7 <3" } }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, "acorn": { "version": "8.0.4", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.0.4.tgz", "integrity": "sha512-XNP0PqF1XD19ZlLKvB7cMmnZswW4C/03pRHgirB30uSJTaS3A3V1/P4sS3HPvFmjoriPCJQs+JDSbm4bL1TxGQ==", "dev": true }, + "acorn-globals": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "dev": true, + "requires": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + }, + "dependencies": { + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true + }, + "acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", + "dev": true + } + } + }, "acorn-jsx": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", @@ -1181,6 +1277,12 @@ "integrity": "sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas=", "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-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -1247,6 +1349,21 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -1262,6 +1379,18 @@ "lodash": "^4.17.14" } }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, "at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -1383,6 +1512,18 @@ } } }, + "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.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, "babel-eslint": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", @@ -1411,6 +1552,15 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, "binary-extensions": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", @@ -1476,6 +1626,32 @@ "fill-range": "^7.0.1" } }, + "browser-env": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/browser-env/-/browser-env-3.3.0.tgz", + "integrity": "sha512-XO+rbi2f9DtuoYgnN/Um80Qz/m033C3fKdUxKbbNs+jCshezRENMlG+QGT9qZyZE4b/KYTwUu6RNMCavGhPdEQ==", + "dev": true, + "requires": { + "window": "4.2.6" + }, + "dependencies": { + "window": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/window/-/window-4.2.6.tgz", + "integrity": "sha512-vk5Uv4hlPkZjUTAUVJUyvJQrbA05T99Qm3CTk0krXHKdoghxV70uPbHK3uGmrI1SfyJpdYncvZ7CbewJ30e9MQ==", + "dev": true, + "requires": { + "jsdom": "13.2.0" + } + } + } + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -1550,6 +1726,12 @@ "quick-lru": "^4.0.1" } }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, "chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", @@ -1729,6 +1911,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", @@ -2960,6 +3151,21 @@ "integrity": "sha512-ytYWn+EmSuthkh+GOKqLpBXsa5mnSBvOgl58IF04zB8LvjQpzRdo1FJkiQiy+HYPMl1xUmcLDqcAhvANzNGDMg==", "dev": true }, + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "cssstyle": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "dev": true, + "requires": { + "cssom": "0.3.x" + } + }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -2989,6 +3195,26 @@ "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", "dev": true }, + "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" + } + }, + "data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + } + }, "date-time": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", @@ -3121,6 +3347,12 @@ "slash": "^3.0.0" } }, + "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 + }, "detect-indent": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz", @@ -3157,6 +3389,15 @@ "esutils": "^2.0.2" } }, + "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" + } + }, "dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -3218,6 +3459,16 @@ "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", "dev": true }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, "editor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/editor/-/editor-1.0.0.tgz", @@ -3330,6 +3581,60 @@ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "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" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "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", + "word-wrap": "~1.2.3" + } + }, + "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 + }, + "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" + } + } + } + }, "eslint": { "version": "7.15.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.15.0.tgz", @@ -3892,6 +4197,12 @@ "strip-final-newline": "^2.0.0" } }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -3914,6 +4225,12 @@ } } }, + "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": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4100,6 +4417,23 @@ "signal-exit": "^3.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.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, "fromentries": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", @@ -4425,6 +4759,15 @@ "pump": "^3.0.0" } }, + "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" + } + }, "gh-pages": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.1.0.tgz", @@ -4672,6 +5015,22 @@ "wordwrap": "^1.0.0" } }, + "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.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, "hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -4744,6 +5103,15 @@ "lru-cache": "^6.0.0" } }, + "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.1" + } + }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -4767,6 +5135,17 @@ "debug": "4" } }, + "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.2.2", + "sshpk": "^1.7.0" + } + }, "https-proxy-agent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", @@ -5400,6 +5779,12 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "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 + }, "istanbul-lib-coverage": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", @@ -5521,6 +5906,54 @@ "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 + }, + "jsdom": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-13.2.0.tgz", + "integrity": "sha512-cG1NtMWO9hWpqRNRR3dSvEQa8bFI6iLlqU2x4kwX51FQjp0qus8T9aBaAO6iGp3DeBrhdwuKxckknohkmfvsFw==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "acorn": "^6.0.4", + "acorn-globals": "^4.3.0", + "array-equal": "^1.0.0", + "cssom": "^0.3.4", + "cssstyle": "^1.1.1", + "data-urls": "^1.1.0", + "domexception": "^1.0.1", + "escodegen": "^1.11.0", + "html-encoding-sniffer": "^1.0.2", + "nwsapi": "^2.0.9", + "parse5": "5.1.0", + "pn": "^1.1.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.5", + "saxes": "^3.1.5", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.5.0", + "w3c-hr-time": "^1.0.1", + "w3c-xmlserializer": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^7.0.0", + "ws": "^6.1.2", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true + } + } + }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -5545,6 +5978,12 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "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.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -5604,6 +6043,24 @@ "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" + } + }, + "just-extend": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.1.tgz", + "integrity": "sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA==", + "dev": true + }, "keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -5759,6 +6216,12 @@ "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", "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 + }, "lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", @@ -5984,6 +6447,21 @@ "picomatch": "^2.0.5" } }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "dev": true + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dev": true, + "requires": { + "mime-db": "1.44.0" + } + }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -6079,6 +6557,19 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "nise": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/nise/-/nise-4.0.4.tgz", + "integrity": "sha512-bTTRUNlemx6deJa+ZyoCUTRvH3liK5+N6VQZ4NIw90AgDXY6iPnsqplNFf6STcj+ePk0H/xqxnP75Lr0J0Fq3A==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^6.0.0", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, "node-fetch": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", @@ -6329,6 +6820,12 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, "nyc": { "version": "15.1.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", @@ -6375,6 +6872,12 @@ } } }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6707,6 +7210,12 @@ "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", "dev": true }, + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "dev": true + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6731,6 +7240,23 @@ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "requires": { + "isarray": "0.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -6743,6 +7269,12 @@ "integrity": "sha512-KGuODSTV6hcgdZvDrIDBUkN0utcAVj1LL7FfGbM0viKTtCHmtZcuEJ+lGqsp0fTFkGqesdtemV2yUSMeyy3ddA==", "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 + }, "picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", @@ -6855,6 +7387,12 @@ "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", "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.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -6903,6 +7441,12 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -6934,6 +7478,12 @@ "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", "dev": true }, + "qs": { + "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 + }, "query-string": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", @@ -7134,6 +7684,54 @@ "is-finite": "^1.0.0" } }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "requires": { + "lodash": "^4.17.19" + } + }, + "request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "dev": true, + "requires": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7262,6 +7860,15 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "saxes": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", + "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", + "dev": true, + "requires": { + "xmlchars": "^2.1.1" + } + }, "semver": { "version": "7.3.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", @@ -7347,6 +7954,21 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, + "sinon": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.2.tgz", + "integrity": "sha512-9Owi+RisvCZpB0bdOVFfL314I6I4YoRlz6Isi4+fr8q8YQsDPoCe5UnmNtKHRThX3negz2bXHWIuiPa42vM8EQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.8.1", + "@sinonjs/fake-timers": "^6.0.1", + "@sinonjs/formatio": "^5.0.1", + "@sinonjs/samsam": "^5.3.0", + "diff": "^4.0.2", + "nise": "^4.0.4", + "supports-color": "^7.1.0" + } + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -7471,6 +8093,23 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, "stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", @@ -7572,6 +8211,12 @@ } } }, + "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-events": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", @@ -7795,6 +8440,12 @@ "has-flag": "^4.0.0" } }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, "table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", @@ -8052,6 +8703,25 @@ "ieee754": "^1.1.13" } }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.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" + } + }, "trim-newlines": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", @@ -8122,6 +8792,21 @@ "tslib": "^1.8.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.0.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 + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -8131,6 +8816,12 @@ "prelude-ls": "^1.2.1" } }, + "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 + }, "type-fest": { "version": "0.18.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", @@ -8299,12 +8990,43 @@ "spdx-expression-parse": "^3.0.0" } }, + "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.2.0" + } + }, "vscode-uri": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz", "integrity": "sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==", "dev": true }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", + "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", + "dev": true, + "requires": { + "domexception": "^1.0.1", + "webidl-conversions": "^4.0.2", + "xml-name-validator": "^3.0.0" + } + }, "wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -8314,12 +9036,55 @@ "defaults": "^1.0.3" } }, + "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 + }, "well-known-symbols": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", "dev": true }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8391,12 +9156,33 @@ "typedarray-to-buffer": "^3.1.5" } }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, "xdg-basedir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", "dev": true }, + "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 + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 3006bcc..237e272 100644 --- a/package.json +++ b/package.json @@ -53,9 +53,9 @@ "doc:html": "typedoc src/ --exclude **/*.spec.ts --target ES6 --mode file --out build/docs", "doc:json": "typedoc src/ --exclude **/*.spec.ts --target ES6 --mode file --json build/docs/typedoc.json", "doc:publish": "gh-pages -m \"[ci skip] Updates\" -d build/docs", - "version": "npx standard-version --no-verify -a", + "version": "npx standard-version --no-verify -a -t ''", "reset-hard": "git clean -dfx && git reset --hard && npm i", - "prepare-release": "run-s reset-hard test doc:html version doc:publish", + "prepare-release": "run-s reset-hard build test doc:html doc:json version doc:publish", "commit": "./node_modules/cz-customizable/standalone.js", "clean": "rm -rf build .nyc_output" }, @@ -71,9 +71,12 @@ "@commitlint/cli": "^11.0.0", "@commitlint/config-conventional": "^11.0.0", "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@types/node": "^14.14.12", + "@types/sinon": "^9.0.9", "@typescript-eslint/eslint-plugin": "^4.0.1", "@typescript-eslint/parser": "^4.0.1", "ava": "^3.12.1", + "browser-env": "^3.3.0", "codecov": "^3.5.0", "commitlint-config-cz": "^0.13.2", "cspell": "^4.1.0", @@ -90,6 +93,7 @@ "nyc": "^15.1.0", "open-cli": "^6.0.1", "prettier": "^2.1.1", + "sinon": "^9.2.2", "standard-version": "^9.0.0", "ts-node": "^9.0.0", "typedoc": "^0.19.2", @@ -118,6 +122,9 @@ "./test/**/*.spec.ts", "./tests/**/*.spec.ts", "!build/module/**" + ], + "require": [ + "./test/_setup-browser-environment.js" ] }, "lint-staged": { diff --git a/src/index.ts b/src/index.ts index c0d7bb6..8685415 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,2 @@ export * from './lib/fine-mq' +export { FineMqPlugin } from './vue/plugin' diff --git a/src/lib/fine-mq.ts b/src/lib/fine-mq.ts index a7d7b52..1c34e3c 100644 --- a/src/lib/fine-mq.ts +++ b/src/lib/fine-mq.ts @@ -4,7 +4,6 @@ import { aliases2mq, json2mq } from './helpers' import { FineMediaQueries, MatchingAliases, - MediaQueryAliases, MediaQueryMatcherHandler, MediaQueryMatchListener, MediaQueryObject, @@ -28,17 +27,19 @@ export const getMatchListener = (mq: Mq, aliasOrMediaQuery: string): MediaQueryM export const setMatchingAliases = (mq: Mq, matchingAliases: MatchingAliases = {}) => (mq.matchingAliases = matchingAliases) -export const createNewMediaQueryMatchListener = ( +export const createMediaQueryMatchListener = ( mq: Mq, aliasOrMediaQuery: string, - callback: MediaQueryMatcherHandler + callback?: MediaQueryMatcherHandler, + forceRegistration?: boolean ): MediaQueryMatchListener => { + if (typeof callback !== 'function') + throw new TypeError('The callback function needs to be defined in order to create a new match listener') + let mediaQuery = getMediaQueryString(mq, aliasOrMediaQuery) if (!mediaQuery) { mediaQuery = json2mq(aliasOrMediaQuery) } - if (typeof callback !== 'function') - throw new TypeError('The callback function needs to be defined in order to create a new match listener') const mqMatcher: MediaQueryMatchListener = { handlers: [], @@ -67,6 +68,13 @@ export const createNewMediaQueryMatchListener = ( }, } + if (forceRegistration) { + if (!(aliasOrMediaQuery in mq.aliases)) { + mq.aliases = { ...mq.aliases, [mediaQuery]: mediaQuery } + } + mq.matchers[mediaQuery] = mqMatcher + } + mqMatcher.handlers.push(callback) mqMatcher.listener.call(undefined, { matches: mqMatcher.matcher.matches, mediaQuery, alias: aliasOrMediaQuery }) // trigger listener immediately mqMatcher.matcher.addEventListener('change', mqMatcher.listener) @@ -82,15 +90,6 @@ export const removeMediaQueryMatchListener = (mq: Mq, aliasOrMediaQuery: string) } } -export const on = (mq: Mq, aliasOrMediaQuery: string, callback: MediaQueryMatcherHandler): void => { - const mediaQuery = getMediaQueryString(mq, aliasOrMediaQuery) - const mqMatcher = mq.matchers[mediaQuery] || createNewMediaQueryMatchListener(mq, aliasOrMediaQuery, callback) - mq.matchers = { - ...mq.matchers, - [mediaQuery]: mqMatcher, - } -} - export const off = (mq: Mq, aliasOrMediaQuery?: string, callback?: MediaQueryMatcherHandler): void => { if (!aliasOrMediaQuery) { // remove all listeners @@ -116,6 +115,17 @@ export const off = (mq: Mq, aliasOrMediaQuery?: string, callback?: MediaQueryMat } } +export const on = (mq: Mq, aliasOrMediaQuery: string, callback: MediaQueryMatcherHandler): (() => void) => { + const mediaQuery = getMediaQueryString(mq, aliasOrMediaQuery) + const mqMatcher = mq.matchers[mediaQuery] || createMediaQueryMatchListener(mq, aliasOrMediaQuery, callback) + mq.matchers = { + ...mq.matchers, + [mediaQuery]: mqMatcher, + } + + return () => off(mq, aliasOrMediaQuery, callback) +} + export const addAlias = (mq: Mq, alias: string | { [key: string]: MediaQueryObject }, mediaQuery?: MediaQueryObject): void => { if (typeof alias === 'string' && mediaQuery) { mq.aliases = { @@ -127,10 +137,14 @@ export const addAlias = (mq: Mq, alias: string | { [key: string]: MediaQueryObje ...mq.aliases, ...aliases2mq(alias), } - } + } // else Trying to register alias "${alias}" with no media query associated. Nothing to do. } export const removeAlias = (mq: Mq, alias: string): void => { + if (!(alias in mq.aliases)) { + // Trying to unregister inexistant alias "${alias}". Nothing to do. + return + } const mqMatcher = getMatchListener(mq, alias) if (mqMatcher) off(mq, alias) @@ -161,60 +175,3 @@ export const createFineMediaQueries = ( removeAlias: (alias: string) => removeAlias(mq, alias), } } - -export const FineMqPlugin = { - install(app: any, options: { aliases?: MediaQueryAliases; defaultMatchingAliases?: MatchingAliases } = {}) { - let hasSetupListeners = false - const defaultLastActiveAlias = Object.keys(options.defaultMatchingAliases ?? {})[0] - - const reactiveSource = app.observable({ - // replace with app.reactive in the next major version to support vue3 - matchingAliases: {}, - lastActiveAlias: defaultLastActiveAlias, - }) - - const fineMq = createFineMediaQueries( - options.aliases || { sm: 680, md: [681, 1024], lg: [1025] }, - options.defaultMatchingAliases - ) - - const onMqMatchEvent: MediaQueryMatcherHandler = ({ matches, alias, mediaQuery }) => { - const aliases = getAliasesForMediaQuery(fineMq.mq, mediaQuery) - const matchingAliases: MatchingAliases = {} - for (const _alias of aliases) { - matchingAliases[_alias] = matches - } - reactiveSource.matchingAliases = { ...reactiveSource.matchingAliases, ...matchingAliases } - if (matches) reactiveSource.lastActiveAlias = alias - } - - app.mixin({ - computed: { - $mq() { - return reactiveSource.matchingAliases - }, - $mqLastActiveAlias() { - return reactiveSource.lastActiveAlias - }, - }, - created() { - if (this.$isServer) { - reactiveSource.matchingAliases = options.defaultMatchingAliases - reactiveSource.lastActiveAlias = defaultLastActiveAlias - } - }, - mounted() { - if (!hasSetupListeners) { - Object.keys(fineMq.mq.aliases).forEach((alias) => fineMq.on(alias, onMqMatchEvent)) - hasSetupListeners = true - } - }, - }) - - // remove this in the next major version to support vue3 - app.prototype.$fineMq = fineMq - - // uncomment this in the next major version to support vue3 - // app.config.globalProperties.$fineMq = fineMq - }, -} diff --git a/src/lib/test/fine-mq.spec.ts b/src/lib/test/fine-mq.spec.ts new file mode 100644 index 0000000..abd5072 --- /dev/null +++ b/src/lib/test/fine-mq.spec.ts @@ -0,0 +1,378 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +import test, { Macro } from 'ava' +import sinon from 'sinon' + +import { + addAlias, + createMediaQueryMatchListener, + getAliasesForMediaQuery, + getMatchListener, + getMediaQueryString, + off, + on, + removeAlias, + removeMediaQueryMatchListener, + setMatchingAliases, +} from '../fine-mq' +import { MediaQueryMatchListener, Mq } from '../types' + +const fakeMatchListener: MediaQueryMatchListener = { + handlers: [], + listener: () => {}, + matcher: window.matchMedia(''), +} + +const mq: Mq = { + aliases: { + an_alias: 'an_mq_value', + alias2: 'an_mq_value', + }, + matchers: { + an_mq_value: fakeMatchListener, + another_mq_value: fakeMatchListener, + }, + matchingAliases: {}, +} + +const getMqStringMacro: Macro<[string, string]> = (t, input, expected) => { + t.is(getMediaQueryString(mq, input), expected) +} + +;[ + ['an_alias', 'an_mq_value'], + ['non_existant_alias', 'non_existant_alias'], +].forEach(([input, expected], index) => { + test( + `${index}. getMediaQueryString returns the value associated to the given alias or returns the alias`, + getMqStringMacro, + input, + expected + ) +}) + +test(`getAliasesForMediaQuery returns all the associated aliases for a media query`, (t) => { + const aliases = getAliasesForMediaQuery(mq, 'an_mq_value') + t.true(aliases.includes('an_alias')) + t.true(aliases.includes('alias2')) +}) + +test(`getAliasesForMediaQuery returns an empty array if the media query is not associated to an alias`, (t) => { + t.deepEqual(getAliasesForMediaQuery(mq, 'noop'), []) +}) + +test(`getMatchListener returns the associated match listener for an alias or media query`, (t) => { + t.is(getMatchListener(mq, 'an_alias'), fakeMatchListener) + t.is(getMatchListener(mq, 'another_mq_value'), fakeMatchListener) +}) + +test(`getMatchListener returns nothing if no match listener exists for an alias or media query`, (t) => { + t.falsy(getMatchListener(mq, 'noop')) +}) + +test(`setMatchingAliases overrides current matching aliases`, (t) => { + const localMq: Mq = { + aliases: {}, + matchers: {}, + matchingAliases: {}, + } + const matchingAliases = { alias1: true, alias2: false } + + t.deepEqual(localMq.matchingAliases, {}) + setMatchingAliases(localMq, matchingAliases) + t.deepEqual(localMq.matchingAliases, matchingAliases) +}) + +test(`addAlias registers the given alias`, (t) => { + const localMq: Mq = { + aliases: {}, + matchers: {}, + matchingAliases: {}, + } + addAlias(localMq, 'alias', 'a media query') + t.true('alias' in localMq.aliases) + t.is(localMq.aliases.alias, 'a media query') +}) + +test(`addAlias registers all the given aliases if an object is passed`, (t) => { + const localMq: Mq = { + aliases: {}, + matchers: {}, + matchingAliases: {}, + } + addAlias(localMq, { alias: 'a media query', alias2: 'another media query' }) + t.true('alias' in localMq.aliases) + t.true('alias2' in localMq.aliases) + t.is(localMq.aliases.alias, 'a media query') + t.is(localMq.aliases.alias2, 'another media query') +}) + +test(`removeAlias unregisters the given alias and remove the associated matcher if any`, (t) => { + const fakeMatchListener: MediaQueryMatchListener = { + handlers: [], + listener: () => {}, + matcher: window.matchMedia(''), + } + const localMq: Mq = { + aliases: { + alias: 'an_mq_value', + alias2: 'an_mq_value', + }, + matchers: { + an_mq_value: fakeMatchListener, + }, + matchingAliases: {}, + } + removeAlias(localMq, 'alias') + t.false('alias' in localMq.aliases) + t.false('an_mq_value' in localMq.matchers) + removeAlias(localMq, 'alias2') + t.false('alias2' in localMq.aliases) +}) + +test(`removeAlias does not unregister the matcher associated to the given media query if passing an existing media query instead of an existing alias`, (t) => { + const fakeMatchListener: MediaQueryMatchListener = { + handlers: [], + listener: () => {}, + matcher: window.matchMedia(''), + } + const localMq: Mq = { + aliases: {}, + matchers: { + an_mq_value: fakeMatchListener, + }, + matchingAliases: {}, + } + removeAlias(localMq, 'an_mq_value') + t.true('an_mq_value' in localMq.matchers) +}) + +test(`removeMediaQueryMatchListener unregisters the matcher associated to the given alias if any`, (t) => { + const fakeMatchListener: MediaQueryMatchListener = { + handlers: [], + listener: () => {}, + matcher: window.matchMedia(''), + } + const localMq: Mq = { + aliases: { + alias: 'an_mq_value', + }, + matchers: { + an_mq_value: fakeMatchListener, + another_mq_value: fakeMatchListener, + }, + matchingAliases: {}, + } + removeMediaQueryMatchListener(localMq, 'alias') + t.true('alias' in localMq.aliases) + t.false('an_mq_value' in localMq.matchers) + t.true('another_mq_value' in localMq.matchers) +}) + +test(`removeMediaQueryMatchListener unregisters the matcher associated to the given media query if any`, (t) => { + const fakeMatchListener: MediaQueryMatchListener = { + handlers: [], + listener: () => {}, + matcher: window.matchMedia(''), + } + const localMq: Mq = { + aliases: {}, + matchers: { + an_mq_value: fakeMatchListener, + another_mq_value: fakeMatchListener, + }, + matchingAliases: {}, + } + removeMediaQueryMatchListener(localMq, 'an_mq_value') + t.false('an_mq_value' in localMq.matchers) + t.true('another_mq_value' in localMq.matchers) +}) + +test('createMediaQueryMatchListener throws if no callback is passed', (t) => { + const localMq: Mq = { + aliases: {}, + matchers: { + an_mq_value: fakeMatchListener, + another_mq_value: fakeMatchListener, + }, + matchingAliases: {}, + } + t.throws(() => createMediaQueryMatchListener(localMq, 'an alias')) +}) + +test('createMediaQueryMatchListener registers the callback passed as a mq listener to the existing given alias', (t) => { + const localMq: Mq = { + aliases: { + alias: 'an_mq_value', + }, + matchers: {}, + matchingAliases: {}, + } + const callback = sinon.fake() + const spy = sinon.spy(window, 'matchMedia') + const newMatcher = createMediaQueryMatchListener(localMq, 'alias', callback) + t.true(spy.calledWith('an_mq_value')) + t.true(newMatcher.handlers.includes(callback)) + t.true(callback.calledWith({ matches: false, mediaQuery: 'an_mq_value', alias: 'alias' })) + t.false(localMq.matchingAliases.alias) + spy.restore() +}) + +test('createMediaQueryMatchListener registers the callback passed as a mq listener to the given media query', (t) => { + const localMq: Mq = { + aliases: {}, + matchers: {}, + matchingAliases: {}, + } + const callback = sinon.fake() + const spy = sinon.spy(window, 'matchMedia') + const newMatcher = createMediaQueryMatchListener(localMq, 'a media query', callback) + t.true(spy.calledWith('a media query')) + t.true(newMatcher.handlers.includes(callback)) + t.true(callback.calledWith({ matches: false, mediaQuery: 'a media query', alias: 'a media query' })) + spy.restore() +}) + +test('createMediaQueryMatchListener: the match listener updates the matchingAliases when called', (t) => { + const localMq: Mq = { + aliases: { + alias: 'an_mq_value', + }, + matchers: {}, + matchingAliases: {}, + } + const callback = sinon.fake() + t.false('alias' in localMq.matchingAliases) + const newMatcher = createMediaQueryMatchListener(localMq, 'alias', callback) + t.false(localMq.matchingAliases.alias) + newMatcher.listener({ matches: true }) + t.true(callback.calledWith({ matches: true, mediaQuery: 'an_mq_value', alias: 'alias' })) + t.true(localMq.matchingAliases.alias) +}) + +test('createMediaQueryMatchListener force registers the media query and matcher', (t) => { + const localMq: Mq = { + aliases: {}, + matchers: {}, + matchingAliases: {}, + } + const callback = sinon.fake() + const newMatcher = createMediaQueryMatchListener(localMq, 'a media query', callback, true) + t.true('a media query' in localMq.aliases) + t.is(localMq.matchers['a media query'], newMatcher) + t.false(localMq.matchingAliases['a media query']) +}) + +test('createMediaQueryMatchListener force registers the matcher but not the alias if it already exists', (t) => { + const localMq: Mq = { + aliases: { + alias: 'a media query', + }, + matchers: {}, + matchingAliases: {}, + } + const callback = sinon.fake() + const newMatcher = createMediaQueryMatchListener(localMq, 'alias', callback, true) + t.false('a media query' in localMq.aliases) + t.is(localMq.matchers['a media query'], newMatcher) + t.false(localMq.matchingAliases['alias']) +}) + +test('on registers the callback passed as a mq listener to the existing given alias', (t) => { + const localMq: Mq = { + aliases: { + alias: 'an_mq_value', + }, + matchers: {}, + matchingAliases: {}, + } + const callback = sinon.fake() + on(localMq, 'alias', callback) + t.true('an_mq_value' in localMq.matchers) + t.true(localMq.matchers.an_mq_value.handlers.includes(callback)) +}) + +test('on return a function that unregister the listener', (t) => { + const localMq: Mq = { + aliases: { + alias: 'an_mq_value', + }, + matchers: {}, + matchingAliases: {}, + } + const callback = sinon.fake() + const unregister = on(localMq, 'alias', callback) + t.true('an_mq_value' in localMq.matchers) + unregister() + t.false('an_mq_value' in localMq.matchers) +}) + +test('off unregisters the listener for the given alias and callback but keeps the other handlers', (t) => { + const callback = sinon.fake() + const localMq: Mq = { + aliases: { + alias: 'an_mq_value', + }, + matchers: { + an_mq_value: { + handlers: [() => {}, callback], + listener: () => {}, + matcher: window.matchMedia(''), + }, + }, + matchingAliases: {}, + } + + const spy = sinon.spy(localMq.matchers.an_mq_value.matcher, 'removeEventListener') + off(localMq, 'alias', callback) + t.false(localMq.matchers.an_mq_value.handlers.includes(callback)) + t.is(localMq.matchers.an_mq_value.handlers.length, 1) + t.is(spy.callCount, 0) + spy.restore() +}) + +test('off the matcher for the given alias and callback if no handlers remains', (t) => { + const callback = sinon.fake() + const localMq: Mq = { + aliases: { + alias: 'an_mq_value', + }, + matchers: { + an_mq_value: { + handlers: [callback], + listener: () => {}, + matcher: window.matchMedia(''), + }, + }, + matchingAliases: {}, + } + + const spy = sinon.spy(localMq.matchers.an_mq_value.matcher, 'removeEventListener') + const listener = localMq.matchers.an_mq_value.listener + off(localMq, 'alias', callback) + t.true(spy.calledWith('change', listener)) + t.false('an_mq_value' in localMq.matchers) + spy.restore() +}) + +test('off unregisters all handlers for the given alias', (t) => { + const localMq: Mq = { + aliases: { + alias: 'an_mq_value', + }, + matchers: { + an_mq_value: { + handlers: [() => {}, () => {}], + listener: () => {}, + matcher: window.matchMedia(''), + }, + }, + matchingAliases: {}, + } + + const spy = sinon.spy(localMq.matchers.an_mq_value.matcher, 'removeEventListener') + const listener = localMq.matchers.an_mq_value.listener + off(localMq, 'alias') + t.false('an_mq_value' in localMq.matchers) + t.true(spy.calledWith('change', listener)) + spy.restore() +}) diff --git a/src/vue/plugin.ts b/src/vue/plugin.ts new file mode 100644 index 0000000..598b499 --- /dev/null +++ b/src/vue/plugin.ts @@ -0,0 +1,59 @@ +import { createFineMediaQueries, getAliasesForMediaQuery } from '../lib/fine-mq' +import { MatchingAliases, MediaQueryAliases, MediaQueryMatcherHandler } from '../lib/types' + +export const FineMqPlugin = { + install(app: any, options: { aliases?: MediaQueryAliases; defaultMatchingAliases?: MatchingAliases } = {}) { + let hasSetupListeners = false + const defaultLastActiveAlias = Object.keys(options.defaultMatchingAliases ?? {})[0] + + const reactiveSource = app.observable({ + // replace with app.reactive in the next major version to support vue3 + matchingAliases: {}, + lastActiveAlias: defaultLastActiveAlias, + }) + + const fineMq = createFineMediaQueries( + options.aliases || { sm: 680, md: [681, 1024], lg: [1025] }, + options.defaultMatchingAliases + ) + + const onMqMatchEvent: MediaQueryMatcherHandler = ({ matches, alias, mediaQuery }) => { + const aliases = getAliasesForMediaQuery(fineMq.mq, mediaQuery) + const matchingAliases: MatchingAliases = {} + for (const _alias of aliases) { + matchingAliases[_alias] = matches + } + reactiveSource.matchingAliases = { ...reactiveSource.matchingAliases, ...matchingAliases } + if (matches) reactiveSource.lastActiveAlias = alias + } + + app.mixin({ + computed: { + $mq() { + return reactiveSource.matchingAliases + }, + $mqLastActiveAlias() { + return reactiveSource.lastActiveAlias + }, + }, + created() { + if (this.$isServer) { + reactiveSource.matchingAliases = options.defaultMatchingAliases + reactiveSource.lastActiveAlias = defaultLastActiveAlias + } + }, + mounted() { + if (!hasSetupListeners) { + Object.keys(fineMq.mq.aliases).forEach((alias) => fineMq.on(alias, onMqMatchEvent)) + hasSetupListeners = true + } + }, + }) + + // remove this in the next major version to support vue3 + app.prototype.$fineMq = fineMq + + // uncomment this in the next major version to support vue3 + // app.config.globalProperties.$fineMq = fineMq + }, +} diff --git a/test/_setup-browser-environment.js b/test/_setup-browser-environment.js new file mode 100644 index 0000000..800e9b7 --- /dev/null +++ b/test/_setup-browser-environment.js @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-var-requires, unicorn/no-null, @typescript-eslint/no-empty-function, no-undef */ +const browserEnv = require('browser-env') +browserEnv() + +Object.defineProperty(window, 'matchMedia', { + writable: true, + value: (query) => ({ + matches: false, + media: query, + onchange: null, + addListener: () => {}, + removeListener: () => {}, + addEventListener: () => {}, + removeEventListener: () => {}, + dispatchEvent: () => {}, + }), +}) diff --git a/tsconfig.json b/tsconfig.json index 007211e..0083b07 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -38,10 +38,10 @@ // "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, "lib": ["es2017", "dom"], - "types": [], + "types": ["node"], "typeRoots": ["node_modules/@types", "src/types"] }, - "include": ["src/**/*.ts"], + "include": ["src/**/*.ts","test/_setup-browser-environment.js"], "exclude": ["node_modules/**"], "compileOnSave": false }