├── .gitignore ├── .prettierrc ├── .travis.yml ├── LICENSE ├── README.md ├── lib └── express-ab.js ├── package-lock.json ├── package.json └── test ├── cookies.js ├── filter.js ├── getVariant.js ├── helpers.js ├── initialization.js ├── passive.js ├── response.js ├── round-robin.js └── weighted.js /.gitignore: -------------------------------------------------------------------------------- 1 | .nyc_output/ 2 | coverage 3 | node_modules 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "trailingComma": "all", 6 | "useTabs": false 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - lts/* 4 | after_script: "npm install coveralls && nyc report --reporter=text-lcov | coveralls" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # express-ab 2 | 3 | [![Build Status][travis-image]][travis-url] 4 | [![Test Coverage][coveralls-image]][coveralls-url] 5 | 6 | Middleware for AB/split/multi-variant testing in [Express](http://expressjs.com/). Allows you to specify multiple variants of an endpoint as a part of an experiment. Remembers which variant the user was assigned using a cookie. 7 | 8 | Supports outputting Google Experiments variables (`experimentId` and `experimentVariant`). 9 | 10 | ## Install 11 | 12 | ```bash 13 | $ npm install express-ab --save 14 | ``` 15 | 16 | ## Usage 17 | 18 | Notice that express-ab requires the [cookie-parser](https://www.npmjs.org/package/cookie-parser) middleware to remember which variant the user was served. 19 | 20 | ```javascript 21 | var express = require('express'); 22 | var cookieParser = require('cookie-parser'); 23 | var ab = require('express-ab'); 24 | 25 | var app = express(); 26 | app.use(cookieParser()); 27 | 28 | var myPageTest = ab.test('my-fancy-test'); 29 | 30 | app.get('/', myPageTest(), function (req, res) { 31 | res.send('variant A'); 32 | }); 33 | 34 | app.get('/', myPageTest(), function (req, res) { 35 | res.send('variant B'); 36 | }); 37 | 38 | app.listen(8080); 39 | ``` 40 | 41 | In example above users will be presented with either 'variant A' or 'variant B'. Distribution will be 50/50 in a round-robin fashion. 42 | 43 | You can add as many alternative endpoints to your test as you like, e.g. also 'variant C' etc. 44 | 45 | ### Weighted distribution 46 | 47 | The function returned by `ab.test()` (assigned to `myPageTest`), has the following arguments: `myPageTest(variantId[, weight])` 48 | 49 | If you prefer to have a custom distribution, you can specify a weight percentage for each variant. This should be in decimal notation, and the sum should be 1. 50 | 51 | ```javascript 52 | app.get('/', myPageTest(null, 0.2), function (req, res) { 53 | res.send('variant A'); 54 | }); 55 | 56 | app.get('/', myPageTest(null, 0.8), function (req, res) { 57 | res.send('variant B'); 58 | }); 59 | ``` 60 | 61 | In this example variant A will be selected 20% of the time, and variant B 80% of the time. 62 | 63 | ### Google Experiments 64 | 65 | If you are using Google Experiments you can add the expriment ID when running the test, and it will be available in the `locals` collection like this: 66 | 67 | ```javascript 68 | var myPageTest = ab.test('my-fancy-test', { id: 'YByMKfprRCStcMvK8zh1yw' }); 69 | 70 | app.get('/', myPageTest(), function (req, res) { 71 | // res.locals.ab.name === 'my-fancy-test' 72 | // res.locals.ab.id === 'YByMKfprRCStcMvK8zh1yw' 73 | // res.locals.ab.variantId === 0 74 | res.send('variant X'); 75 | }); 76 | ``` 77 | 78 | To use it in your front end, you can expose the `ab` object e.g. on `window`. Then you have to notify Google Analytics that you are running an experiment by setting the following vars: 79 | 80 | ga('set', 'expId', window.ab.id); 81 | ga('set', 'expVar', window.ab.variantId); 82 | 83 | More about setting the experiment variant in Google Analytics is [explained here](https://developers.google.com/analytics/devguides/collection/analyticsjs/experiments). 84 | 85 | 86 | ### Get variant in other routes 87 | 88 | If you need the selected variant in other routes not specifically part of the AB test, you can use the middleware function `getVariant()` on the returned test function (assigned to `myPageTest`). 89 | 90 | ```javascript 91 | app.get('/somepage', myPageTest.getVariant, function (req, res) { 92 | res.send('variant ' + res.locals.ab.variantId); 93 | }); 94 | ``` 95 | 96 | ### Usage as passive middleware 97 | 98 | If you need the variant information in many routes in your application, and need cookies to be assigned for any/all of them, you can create your variants as general purpose middleware instead of attaching it to specific routes. 99 | 100 | ```javascript 101 | var variants = ['A', 'B', 'C']; 102 | for (var i = 0; i < variants.length; i++) { 103 | app.use(myPageTest(variants[i])); 104 | } 105 | 106 | app.get('/somepage', myPageTest.getVariant, function(req, res) { 107 | res.send('variant ' + res.locals.ab.variantId); 108 | }); 109 | ``` 110 | 111 | ### Disable cookie 112 | 113 | If you do not want the user to be sent to the same variant in the test on every return, you can disable cookies like this: 114 | 115 | ```javascript 116 | var myPageTest = ab.test('my-fancy-test', { cookie: false }); 117 | ``` 118 | 119 | Or you can do it for all tests in the constructor: 120 | 121 | ```javascript 122 | var ab = require('express-ab')({ cookie: false }); 123 | ``` 124 | 125 | 126 | ### Skip route if part of another test 127 | 128 | If you are running multiple tests, you can skip routes using `ab.filter(test)`. To create a new test only for users not in the previous test, the code could look something like this: 129 | 130 | ```javascript 131 | var abTest1 = ab.test('filter-test-1'); 132 | var abTest2 = ab.test('filter-test-2'); 133 | 134 | app.get('/', ab.filter(abTest1), abTest2(), helpers.send('2A')); 135 | app.get('/', ab.filter(abTest1), abTest2(), helpers.send('2B')); 136 | app.get('/', helpers.send('fallthrough2')); 137 | ``` 138 | 139 | In this case, if a user is already in `abTest1`, he will not be able to be in `abTest2` as well. Just remember to include a fall through route. 140 | 141 | ## Credits 142 | 143 | This project was inspired by [abn](https://github.com/NoumanSaleem/abn) by [NoumanSaleem](https://github.com/NoumanSaleem). express-ab removes external dependencies and adds support for Google Experiments variables. 144 | 145 | [travis-image]: https://img.shields.io/travis/omichelsen/express-ab/master.svg 146 | [travis-url]: https://travis-ci.org/omichelsen/express-ab 147 | [coveralls-image]: https://img.shields.io/coveralls/omichelsen/express-ab/master.svg 148 | [coveralls-url]: https://coveralls.io/r/omichelsen/express-ab?branch=master 149 | -------------------------------------------------------------------------------- /lib/express-ab.js: -------------------------------------------------------------------------------- 1 | let defaults = { 2 | cookie: { 3 | name: 'ab', 4 | maxAge: 31536000000, 5 | }, 6 | }; 7 | 8 | function ab(opts) { 9 | defaults = { ...defaults, ...opts }; 10 | return ab; 11 | } 12 | 13 | function isWeightDefined(weight) { 14 | return typeof weight === 'number' && weight >= 0 && weight <= 1; 15 | } 16 | 17 | ab.test = function(testName, opts) { 18 | if (!testName) { 19 | throw new Error('.test() requires first parameter "name" (type string)'); 20 | } 21 | 22 | const test = {}; 23 | const options = { ...defaults, ...opts }; 24 | 25 | function getCookie(req) { 26 | if (options.cookie && req.cookies) { 27 | try { 28 | return JSON.parse(req.cookies[options.cookie.name] || '{}'); 29 | } catch (error) {} 30 | } 31 | } 32 | 33 | function setCookie(res, variant, cookie) { 34 | cookie[testName] = variant; 35 | res.cookie(options.cookie.name, JSON.stringify(cookie), options.cookie); 36 | } 37 | 38 | function respond(res, next, variant, cookie, skip) { 39 | if (!skip) { 40 | res.locals.ab = { 41 | name: testName, 42 | id: options.id, 43 | variantId: variant, 44 | }; 45 | if (cookie) { 46 | setCookie(res, variant, cookie); 47 | } 48 | } 49 | next(skip); 50 | } 51 | 52 | function create(variant, weight) { 53 | variant = variant != null ? variant : Object.keys(test).length; 54 | test[variant] = 0; 55 | 56 | return function(req, res, next) { 57 | const current = test[variant]; 58 | const cookie = getCookie(req); 59 | const done = respond.bind(null, res, next, variant, cookie); 60 | let skip; 61 | let keys; 62 | 63 | if (res.locals.ab) { 64 | return done('route'); 65 | } 66 | 67 | if (cookie && cookie.hasOwnProperty(testName)) { 68 | return cookie[testName] === variant ? done() : done('route'); 69 | } 70 | 71 | if (isWeightDefined(weight) && !req.ab) { 72 | req.ab = { 73 | random: Math.random(), 74 | weightSum: 0, 75 | }; 76 | } 77 | 78 | if (isWeightDefined(weight)) { 79 | req.ab.weightSum += weight; 80 | skip = req.ab.random > req.ab.weightSum; 81 | } else if (req.ab) { 82 | skip = false; 83 | } else { 84 | keys = Object.keys(test); 85 | skip = keys.some(function(key) { 86 | return test[key] < current; 87 | }); 88 | } 89 | if (skip) return done('route'); 90 | 91 | test[variant]++; 92 | done(); 93 | }; 94 | } 95 | 96 | create.getVariant = function(req, res, next) { 97 | const cookie = getCookie(req); 98 | if (cookie && cookie.hasOwnProperty(testName)) { 99 | return respond(res, next, cookie[testName]); 100 | } 101 | next(); 102 | }; 103 | 104 | return create; 105 | }; 106 | 107 | ab.filter = function(test) { 108 | return function(req, res, next) { 109 | const _res = { locals: {} }; 110 | test.getVariant(req, _res, function() { 111 | return !_res.locals.ab ? next() : next('route'); 112 | }); 113 | }; 114 | }; 115 | 116 | module.exports = ab; 117 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-ab", 3 | "version": "1.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.10.4", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", 10 | "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.4" 14 | } 15 | }, 16 | "@babel/core": { 17 | "version": "7.12.3", 18 | "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", 19 | "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", 20 | "dev": true, 21 | "requires": { 22 | "@babel/code-frame": "^7.10.4", 23 | "@babel/generator": "^7.12.1", 24 | "@babel/helper-module-transforms": "^7.12.1", 25 | "@babel/helpers": "^7.12.1", 26 | "@babel/parser": "^7.12.3", 27 | "@babel/template": "^7.10.4", 28 | "@babel/traverse": "^7.12.1", 29 | "@babel/types": "^7.12.1", 30 | "convert-source-map": "^1.7.0", 31 | "debug": "^4.1.0", 32 | "gensync": "^1.0.0-beta.1", 33 | "json5": "^2.1.2", 34 | "lodash": "^4.17.19", 35 | "resolve": "^1.3.2", 36 | "semver": "^5.4.1", 37 | "source-map": "^0.5.0" 38 | }, 39 | "dependencies": { 40 | "debug": { 41 | "version": "4.2.0", 42 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", 43 | "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", 44 | "dev": true, 45 | "requires": { 46 | "ms": "2.1.2" 47 | } 48 | }, 49 | "ms": { 50 | "version": "2.1.2", 51 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 52 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 53 | "dev": true 54 | }, 55 | "semver": { 56 | "version": "5.7.1", 57 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 58 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 59 | "dev": true 60 | } 61 | } 62 | }, 63 | "@babel/generator": { 64 | "version": "7.12.1", 65 | "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz", 66 | "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==", 67 | "dev": true, 68 | "requires": { 69 | "@babel/types": "^7.12.1", 70 | "jsesc": "^2.5.1", 71 | "source-map": "^0.5.0" 72 | } 73 | }, 74 | "@babel/helper-function-name": { 75 | "version": "7.10.4", 76 | "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", 77 | "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", 78 | "dev": true, 79 | "requires": { 80 | "@babel/helper-get-function-arity": "^7.10.4", 81 | "@babel/template": "^7.10.4", 82 | "@babel/types": "^7.10.4" 83 | } 84 | }, 85 | "@babel/helper-get-function-arity": { 86 | "version": "7.10.4", 87 | "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", 88 | "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", 89 | "dev": true, 90 | "requires": { 91 | "@babel/types": "^7.10.4" 92 | } 93 | }, 94 | "@babel/helper-member-expression-to-functions": { 95 | "version": "7.12.1", 96 | "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", 97 | "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", 98 | "dev": true, 99 | "requires": { 100 | "@babel/types": "^7.12.1" 101 | } 102 | }, 103 | "@babel/helper-module-imports": { 104 | "version": "7.12.1", 105 | "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.1.tgz", 106 | "integrity": "sha512-ZeC1TlMSvikvJNy1v/wPIazCu3NdOwgYZLIkmIyAsGhqkNpiDoQQRmaCK8YP4Pq3GPTLPV9WXaPCJKvx06JxKA==", 107 | "dev": true, 108 | "requires": { 109 | "@babel/types": "^7.12.1" 110 | } 111 | }, 112 | "@babel/helper-module-transforms": { 113 | "version": "7.12.1", 114 | "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", 115 | "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", 116 | "dev": true, 117 | "requires": { 118 | "@babel/helper-module-imports": "^7.12.1", 119 | "@babel/helper-replace-supers": "^7.12.1", 120 | "@babel/helper-simple-access": "^7.12.1", 121 | "@babel/helper-split-export-declaration": "^7.11.0", 122 | "@babel/helper-validator-identifier": "^7.10.4", 123 | "@babel/template": "^7.10.4", 124 | "@babel/traverse": "^7.12.1", 125 | "@babel/types": "^7.12.1", 126 | "lodash": "^4.17.19" 127 | } 128 | }, 129 | "@babel/helper-optimise-call-expression": { 130 | "version": "7.10.4", 131 | "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", 132 | "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", 133 | "dev": true, 134 | "requires": { 135 | "@babel/types": "^7.10.4" 136 | } 137 | }, 138 | "@babel/helper-replace-supers": { 139 | "version": "7.12.1", 140 | "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.1.tgz", 141 | "integrity": "sha512-zJjTvtNJnCFsCXVi5rUInstLd/EIVNmIKA1Q9ynESmMBWPWd+7sdR+G4/wdu+Mppfep0XLyG2m7EBPvjCeFyrw==", 142 | "dev": true, 143 | "requires": { 144 | "@babel/helper-member-expression-to-functions": "^7.12.1", 145 | "@babel/helper-optimise-call-expression": "^7.10.4", 146 | "@babel/traverse": "^7.12.1", 147 | "@babel/types": "^7.12.1" 148 | } 149 | }, 150 | "@babel/helper-simple-access": { 151 | "version": "7.12.1", 152 | "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", 153 | "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", 154 | "dev": true, 155 | "requires": { 156 | "@babel/types": "^7.12.1" 157 | } 158 | }, 159 | "@babel/helper-split-export-declaration": { 160 | "version": "7.11.0", 161 | "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", 162 | "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", 163 | "dev": true, 164 | "requires": { 165 | "@babel/types": "^7.11.0" 166 | } 167 | }, 168 | "@babel/helper-validator-identifier": { 169 | "version": "7.10.4", 170 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", 171 | "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", 172 | "dev": true 173 | }, 174 | "@babel/helpers": { 175 | "version": "7.12.1", 176 | "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.1.tgz", 177 | "integrity": "sha512-9JoDSBGoWtmbay98efmT2+mySkwjzeFeAL9BuWNoVQpkPFQF8SIIFUfY5os9u8wVzglzoiPRSW7cuJmBDUt43g==", 178 | "dev": true, 179 | "requires": { 180 | "@babel/template": "^7.10.4", 181 | "@babel/traverse": "^7.12.1", 182 | "@babel/types": "^7.12.1" 183 | } 184 | }, 185 | "@babel/highlight": { 186 | "version": "7.10.4", 187 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", 188 | "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", 189 | "dev": true, 190 | "requires": { 191 | "@babel/helper-validator-identifier": "^7.10.4", 192 | "chalk": "^2.0.0", 193 | "js-tokens": "^4.0.0" 194 | } 195 | }, 196 | "@babel/parser": { 197 | "version": "7.12.3", 198 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.3.tgz", 199 | "integrity": "sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==", 200 | "dev": true 201 | }, 202 | "@babel/template": { 203 | "version": "7.10.4", 204 | "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", 205 | "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", 206 | "dev": true, 207 | "requires": { 208 | "@babel/code-frame": "^7.10.4", 209 | "@babel/parser": "^7.10.4", 210 | "@babel/types": "^7.10.4" 211 | } 212 | }, 213 | "@babel/traverse": { 214 | "version": "7.12.1", 215 | "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.1.tgz", 216 | "integrity": "sha512-MA3WPoRt1ZHo2ZmoGKNqi20YnPt0B1S0GTZEPhhd+hw2KGUzBlHuVunj6K4sNuK+reEvyiPwtp0cpaqLzJDmAw==", 217 | "dev": true, 218 | "requires": { 219 | "@babel/code-frame": "^7.10.4", 220 | "@babel/generator": "^7.12.1", 221 | "@babel/helper-function-name": "^7.10.4", 222 | "@babel/helper-split-export-declaration": "^7.11.0", 223 | "@babel/parser": "^7.12.1", 224 | "@babel/types": "^7.12.1", 225 | "debug": "^4.1.0", 226 | "globals": "^11.1.0", 227 | "lodash": "^4.17.19" 228 | }, 229 | "dependencies": { 230 | "debug": { 231 | "version": "4.2.0", 232 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", 233 | "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", 234 | "dev": true, 235 | "requires": { 236 | "ms": "2.1.2" 237 | } 238 | }, 239 | "ms": { 240 | "version": "2.1.2", 241 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 242 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 243 | "dev": true 244 | } 245 | } 246 | }, 247 | "@babel/types": { 248 | "version": "7.12.1", 249 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz", 250 | "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==", 251 | "dev": true, 252 | "requires": { 253 | "@babel/helper-validator-identifier": "^7.10.4", 254 | "lodash": "^4.17.19", 255 | "to-fast-properties": "^2.0.0" 256 | } 257 | }, 258 | "@istanbuljs/load-nyc-config": { 259 | "version": "1.1.0", 260 | "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", 261 | "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", 262 | "dev": true, 263 | "requires": { 264 | "camelcase": "^5.3.1", 265 | "find-up": "^4.1.0", 266 | "get-package-type": "^0.1.0", 267 | "js-yaml": "^3.13.1", 268 | "resolve-from": "^5.0.0" 269 | }, 270 | "dependencies": { 271 | "find-up": { 272 | "version": "4.1.0", 273 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", 274 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", 275 | "dev": true, 276 | "requires": { 277 | "locate-path": "^5.0.0", 278 | "path-exists": "^4.0.0" 279 | } 280 | }, 281 | "locate-path": { 282 | "version": "5.0.0", 283 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", 284 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", 285 | "dev": true, 286 | "requires": { 287 | "p-locate": "^4.1.0" 288 | } 289 | }, 290 | "p-locate": { 291 | "version": "4.1.0", 292 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", 293 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", 294 | "dev": true, 295 | "requires": { 296 | "p-limit": "^2.2.0" 297 | } 298 | } 299 | } 300 | }, 301 | "@istanbuljs/schema": { 302 | "version": "0.1.2", 303 | "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", 304 | "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", 305 | "dev": true 306 | }, 307 | "@ungap/promise-all-settled": { 308 | "version": "1.1.2", 309 | "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", 310 | "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", 311 | "dev": true 312 | }, 313 | "accepts": { 314 | "version": "1.3.7", 315 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 316 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 317 | "dev": true, 318 | "requires": { 319 | "mime-types": "~2.1.24", 320 | "negotiator": "0.6.2" 321 | } 322 | }, 323 | "aggregate-error": { 324 | "version": "3.1.0", 325 | "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", 326 | "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", 327 | "dev": true, 328 | "requires": { 329 | "clean-stack": "^2.0.0", 330 | "indent-string": "^4.0.0" 331 | } 332 | }, 333 | "ansi-colors": { 334 | "version": "4.1.1", 335 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 336 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 337 | "dev": true 338 | }, 339 | "ansi-regex": { 340 | "version": "3.0.0", 341 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 342 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 343 | "dev": true 344 | }, 345 | "ansi-styles": { 346 | "version": "3.2.1", 347 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 348 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 349 | "dev": true, 350 | "requires": { 351 | "color-convert": "^1.9.0" 352 | } 353 | }, 354 | "anymatch": { 355 | "version": "3.1.1", 356 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", 357 | "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", 358 | "dev": true, 359 | "requires": { 360 | "normalize-path": "^3.0.0", 361 | "picomatch": "^2.0.4" 362 | } 363 | }, 364 | "append-transform": { 365 | "version": "2.0.0", 366 | "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", 367 | "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", 368 | "dev": true, 369 | "requires": { 370 | "default-require-extensions": "^3.0.0" 371 | } 372 | }, 373 | "archy": { 374 | "version": "1.0.0", 375 | "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", 376 | "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", 377 | "dev": true 378 | }, 379 | "argparse": { 380 | "version": "1.0.10", 381 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 382 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 383 | "dev": true, 384 | "requires": { 385 | "sprintf-js": "~1.0.2" 386 | } 387 | }, 388 | "array-flatten": { 389 | "version": "1.1.1", 390 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 391 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", 392 | "dev": true 393 | }, 394 | "asynckit": { 395 | "version": "0.4.0", 396 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 397 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", 398 | "dev": true 399 | }, 400 | "balanced-match": { 401 | "version": "1.0.0", 402 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 403 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 404 | "dev": true 405 | }, 406 | "binary-extensions": { 407 | "version": "2.1.0", 408 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", 409 | "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", 410 | "dev": true 411 | }, 412 | "body-parser": { 413 | "version": "1.19.0", 414 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 415 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 416 | "dev": true, 417 | "requires": { 418 | "bytes": "3.1.0", 419 | "content-type": "~1.0.4", 420 | "debug": "2.6.9", 421 | "depd": "~1.1.2", 422 | "http-errors": "1.7.2", 423 | "iconv-lite": "0.4.24", 424 | "on-finished": "~2.3.0", 425 | "qs": "6.7.0", 426 | "raw-body": "2.4.0", 427 | "type-is": "~1.6.17" 428 | } 429 | }, 430 | "brace-expansion": { 431 | "version": "1.1.11", 432 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 433 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 434 | "dev": true, 435 | "requires": { 436 | "balanced-match": "^1.0.0", 437 | "concat-map": "0.0.1" 438 | } 439 | }, 440 | "braces": { 441 | "version": "3.0.2", 442 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 443 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 444 | "dev": true, 445 | "requires": { 446 | "fill-range": "^7.0.1" 447 | } 448 | }, 449 | "browser-stdout": { 450 | "version": "1.3.1", 451 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 452 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 453 | "dev": true 454 | }, 455 | "bytes": { 456 | "version": "3.1.0", 457 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 458 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", 459 | "dev": true 460 | }, 461 | "caching-transform": { 462 | "version": "4.0.0", 463 | "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", 464 | "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", 465 | "dev": true, 466 | "requires": { 467 | "hasha": "^5.0.0", 468 | "make-dir": "^3.0.0", 469 | "package-hash": "^4.0.0", 470 | "write-file-atomic": "^3.0.0" 471 | } 472 | }, 473 | "camelcase": { 474 | "version": "5.3.1", 475 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 476 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", 477 | "dev": true 478 | }, 479 | "chalk": { 480 | "version": "2.4.2", 481 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 482 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 483 | "dev": true, 484 | "requires": { 485 | "ansi-styles": "^3.2.1", 486 | "escape-string-regexp": "^1.0.5", 487 | "supports-color": "^5.3.0" 488 | }, 489 | "dependencies": { 490 | "supports-color": { 491 | "version": "5.5.0", 492 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 493 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 494 | "dev": true, 495 | "requires": { 496 | "has-flag": "^3.0.0" 497 | } 498 | } 499 | } 500 | }, 501 | "chokidar": { 502 | "version": "3.4.3", 503 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", 504 | "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", 505 | "dev": true, 506 | "requires": { 507 | "anymatch": "~3.1.1", 508 | "braces": "~3.0.2", 509 | "fsevents": "~2.1.2", 510 | "glob-parent": "~5.1.0", 511 | "is-binary-path": "~2.1.0", 512 | "is-glob": "~4.0.1", 513 | "normalize-path": "~3.0.0", 514 | "readdirp": "~3.5.0" 515 | } 516 | }, 517 | "clean-stack": { 518 | "version": "2.2.0", 519 | "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", 520 | "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", 521 | "dev": true 522 | }, 523 | "cliui": { 524 | "version": "5.0.0", 525 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", 526 | "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", 527 | "dev": true, 528 | "requires": { 529 | "string-width": "^3.1.0", 530 | "strip-ansi": "^5.2.0", 531 | "wrap-ansi": "^5.1.0" 532 | }, 533 | "dependencies": { 534 | "ansi-regex": { 535 | "version": "4.1.0", 536 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 537 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 538 | "dev": true 539 | }, 540 | "string-width": { 541 | "version": "3.1.0", 542 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 543 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 544 | "dev": true, 545 | "requires": { 546 | "emoji-regex": "^7.0.1", 547 | "is-fullwidth-code-point": "^2.0.0", 548 | "strip-ansi": "^5.1.0" 549 | } 550 | }, 551 | "strip-ansi": { 552 | "version": "5.2.0", 553 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 554 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 555 | "dev": true, 556 | "requires": { 557 | "ansi-regex": "^4.1.0" 558 | } 559 | } 560 | } 561 | }, 562 | "color-convert": { 563 | "version": "1.9.3", 564 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 565 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 566 | "dev": true, 567 | "requires": { 568 | "color-name": "1.1.3" 569 | } 570 | }, 571 | "color-name": { 572 | "version": "1.1.3", 573 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 574 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 575 | "dev": true 576 | }, 577 | "combined-stream": { 578 | "version": "1.0.8", 579 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 580 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 581 | "dev": true, 582 | "requires": { 583 | "delayed-stream": "~1.0.0" 584 | } 585 | }, 586 | "commondir": { 587 | "version": "1.0.1", 588 | "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", 589 | "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", 590 | "dev": true 591 | }, 592 | "component-emitter": { 593 | "version": "1.3.0", 594 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", 595 | "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", 596 | "dev": true 597 | }, 598 | "concat-map": { 599 | "version": "0.0.1", 600 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 601 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 602 | "dev": true 603 | }, 604 | "content-disposition": { 605 | "version": "0.5.3", 606 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 607 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 608 | "dev": true, 609 | "requires": { 610 | "safe-buffer": "5.1.2" 611 | } 612 | }, 613 | "content-type": { 614 | "version": "1.0.4", 615 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 616 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", 617 | "dev": true 618 | }, 619 | "convert-source-map": { 620 | "version": "1.7.0", 621 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", 622 | "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", 623 | "dev": true, 624 | "requires": { 625 | "safe-buffer": "~5.1.1" 626 | } 627 | }, 628 | "cookie": { 629 | "version": "0.4.0", 630 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 631 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", 632 | "dev": true 633 | }, 634 | "cookie-parser": { 635 | "version": "1.4.5", 636 | "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz", 637 | "integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==", 638 | "dev": true, 639 | "requires": { 640 | "cookie": "0.4.0", 641 | "cookie-signature": "1.0.6" 642 | } 643 | }, 644 | "cookie-signature": { 645 | "version": "1.0.6", 646 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 647 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", 648 | "dev": true 649 | }, 650 | "cookiejar": { 651 | "version": "2.1.2", 652 | "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", 653 | "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", 654 | "dev": true 655 | }, 656 | "cross-spawn": { 657 | "version": "7.0.3", 658 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 659 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 660 | "dev": true, 661 | "requires": { 662 | "path-key": "^3.1.0", 663 | "shebang-command": "^2.0.0", 664 | "which": "^2.0.1" 665 | } 666 | }, 667 | "debug": { 668 | "version": "2.6.9", 669 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 670 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 671 | "dev": true, 672 | "requires": { 673 | "ms": "2.0.0" 674 | } 675 | }, 676 | "decamelize": { 677 | "version": "1.2.0", 678 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 679 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 680 | "dev": true 681 | }, 682 | "default-require-extensions": { 683 | "version": "3.0.0", 684 | "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", 685 | "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", 686 | "dev": true, 687 | "requires": { 688 | "strip-bom": "^4.0.0" 689 | } 690 | }, 691 | "delayed-stream": { 692 | "version": "1.0.0", 693 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 694 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", 695 | "dev": true 696 | }, 697 | "depd": { 698 | "version": "1.1.2", 699 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 700 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", 701 | "dev": true 702 | }, 703 | "destroy": { 704 | "version": "1.0.4", 705 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 706 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", 707 | "dev": true 708 | }, 709 | "diff": { 710 | "version": "4.0.2", 711 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 712 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 713 | "dev": true 714 | }, 715 | "ee-first": { 716 | "version": "1.1.1", 717 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 718 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", 719 | "dev": true 720 | }, 721 | "emoji-regex": { 722 | "version": "7.0.3", 723 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 724 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", 725 | "dev": true 726 | }, 727 | "encodeurl": { 728 | "version": "1.0.2", 729 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 730 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", 731 | "dev": true 732 | }, 733 | "es6-error": { 734 | "version": "4.1.1", 735 | "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", 736 | "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", 737 | "dev": true 738 | }, 739 | "escape-html": { 740 | "version": "1.0.3", 741 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 742 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", 743 | "dev": true 744 | }, 745 | "escape-string-regexp": { 746 | "version": "1.0.5", 747 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 748 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 749 | "dev": true 750 | }, 751 | "esprima": { 752 | "version": "4.0.1", 753 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 754 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 755 | "dev": true 756 | }, 757 | "etag": { 758 | "version": "1.8.1", 759 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 760 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", 761 | "dev": true 762 | }, 763 | "express": { 764 | "version": "4.17.1", 765 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 766 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 767 | "dev": true, 768 | "requires": { 769 | "accepts": "~1.3.7", 770 | "array-flatten": "1.1.1", 771 | "body-parser": "1.19.0", 772 | "content-disposition": "0.5.3", 773 | "content-type": "~1.0.4", 774 | "cookie": "0.4.0", 775 | "cookie-signature": "1.0.6", 776 | "debug": "2.6.9", 777 | "depd": "~1.1.2", 778 | "encodeurl": "~1.0.2", 779 | "escape-html": "~1.0.3", 780 | "etag": "~1.8.1", 781 | "finalhandler": "~1.1.2", 782 | "fresh": "0.5.2", 783 | "merge-descriptors": "1.0.1", 784 | "methods": "~1.1.2", 785 | "on-finished": "~2.3.0", 786 | "parseurl": "~1.3.3", 787 | "path-to-regexp": "0.1.7", 788 | "proxy-addr": "~2.0.5", 789 | "qs": "6.7.0", 790 | "range-parser": "~1.2.1", 791 | "safe-buffer": "5.1.2", 792 | "send": "0.17.1", 793 | "serve-static": "1.14.1", 794 | "setprototypeof": "1.1.1", 795 | "statuses": "~1.5.0", 796 | "type-is": "~1.6.18", 797 | "utils-merge": "1.0.1", 798 | "vary": "~1.1.2" 799 | }, 800 | "dependencies": { 801 | "cookie": { 802 | "version": "0.4.0", 803 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 804 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", 805 | "dev": true 806 | } 807 | } 808 | }, 809 | "fast-safe-stringify": { 810 | "version": "2.0.7", 811 | "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", 812 | "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", 813 | "dev": true 814 | }, 815 | "fill-range": { 816 | "version": "7.0.1", 817 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 818 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 819 | "dev": true, 820 | "requires": { 821 | "to-regex-range": "^5.0.1" 822 | } 823 | }, 824 | "finalhandler": { 825 | "version": "1.1.2", 826 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 827 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 828 | "dev": true, 829 | "requires": { 830 | "debug": "2.6.9", 831 | "encodeurl": "~1.0.2", 832 | "escape-html": "~1.0.3", 833 | "on-finished": "~2.3.0", 834 | "parseurl": "~1.3.3", 835 | "statuses": "~1.5.0", 836 | "unpipe": "~1.0.0" 837 | } 838 | }, 839 | "find-cache-dir": { 840 | "version": "3.3.1", 841 | "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", 842 | "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", 843 | "dev": true, 844 | "requires": { 845 | "commondir": "^1.0.1", 846 | "make-dir": "^3.0.2", 847 | "pkg-dir": "^4.1.0" 848 | } 849 | }, 850 | "find-up": { 851 | "version": "5.0.0", 852 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 853 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 854 | "dev": true, 855 | "requires": { 856 | "locate-path": "^6.0.0", 857 | "path-exists": "^4.0.0" 858 | } 859 | }, 860 | "flat": { 861 | "version": "5.0.2", 862 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 863 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 864 | "dev": true 865 | }, 866 | "foreground-child": { 867 | "version": "2.0.0", 868 | "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", 869 | "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", 870 | "dev": true, 871 | "requires": { 872 | "cross-spawn": "^7.0.0", 873 | "signal-exit": "^3.0.2" 874 | } 875 | }, 876 | "form-data": { 877 | "version": "3.0.0", 878 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", 879 | "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", 880 | "dev": true, 881 | "requires": { 882 | "asynckit": "^0.4.0", 883 | "combined-stream": "^1.0.8", 884 | "mime-types": "^2.1.12" 885 | } 886 | }, 887 | "formidable": { 888 | "version": "1.2.2", 889 | "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", 890 | "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==", 891 | "dev": true 892 | }, 893 | "forwarded": { 894 | "version": "0.1.2", 895 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 896 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", 897 | "dev": true 898 | }, 899 | "fresh": { 900 | "version": "0.5.2", 901 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 902 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", 903 | "dev": true 904 | }, 905 | "fromentries": { 906 | "version": "1.3.0", 907 | "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.0.tgz", 908 | "integrity": "sha512-+pKvlQHvpxxSTF+tWZ4DjxD0Sz4G26EjAP4z7D2k8VLJ19hrLbSgaQLx/u2mVQn7hiA2s/3DyutOyFwTuDsRgA==", 909 | "dev": true 910 | }, 911 | "fs.realpath": { 912 | "version": "1.0.0", 913 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 914 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 915 | "dev": true 916 | }, 917 | "fsevents": { 918 | "version": "2.1.3", 919 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", 920 | "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", 921 | "dev": true, 922 | "optional": true 923 | }, 924 | "function-bind": { 925 | "version": "1.1.1", 926 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 927 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 928 | "dev": true 929 | }, 930 | "gensync": { 931 | "version": "1.0.0-beta.1", 932 | "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", 933 | "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", 934 | "dev": true 935 | }, 936 | "get-caller-file": { 937 | "version": "2.0.5", 938 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 939 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 940 | "dev": true 941 | }, 942 | "get-package-type": { 943 | "version": "0.1.0", 944 | "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", 945 | "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", 946 | "dev": true 947 | }, 948 | "glob": { 949 | "version": "7.1.6", 950 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 951 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 952 | "dev": true, 953 | "requires": { 954 | "fs.realpath": "^1.0.0", 955 | "inflight": "^1.0.4", 956 | "inherits": "2", 957 | "minimatch": "^3.0.4", 958 | "once": "^1.3.0", 959 | "path-is-absolute": "^1.0.0" 960 | } 961 | }, 962 | "glob-parent": { 963 | "version": "5.1.1", 964 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", 965 | "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", 966 | "dev": true, 967 | "requires": { 968 | "is-glob": "^4.0.1" 969 | } 970 | }, 971 | "globals": { 972 | "version": "11.12.0", 973 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", 974 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", 975 | "dev": true 976 | }, 977 | "graceful-fs": { 978 | "version": "4.2.4", 979 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", 980 | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", 981 | "dev": true 982 | }, 983 | "growl": { 984 | "version": "1.10.5", 985 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 986 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 987 | "dev": true 988 | }, 989 | "has": { 990 | "version": "1.0.3", 991 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 992 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 993 | "dev": true, 994 | "requires": { 995 | "function-bind": "^1.1.1" 996 | } 997 | }, 998 | "has-flag": { 999 | "version": "3.0.0", 1000 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 1001 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 1002 | "dev": true 1003 | }, 1004 | "hasha": { 1005 | "version": "5.2.2", 1006 | "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", 1007 | "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", 1008 | "dev": true, 1009 | "requires": { 1010 | "is-stream": "^2.0.0", 1011 | "type-fest": "^0.8.0" 1012 | } 1013 | }, 1014 | "he": { 1015 | "version": "1.2.0", 1016 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 1017 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 1018 | "dev": true 1019 | }, 1020 | "html-escaper": { 1021 | "version": "2.0.2", 1022 | "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", 1023 | "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", 1024 | "dev": true 1025 | }, 1026 | "http-errors": { 1027 | "version": "1.7.2", 1028 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 1029 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 1030 | "dev": true, 1031 | "requires": { 1032 | "depd": "~1.1.2", 1033 | "inherits": "2.0.3", 1034 | "setprototypeof": "1.1.1", 1035 | "statuses": ">= 1.5.0 < 2", 1036 | "toidentifier": "1.0.0" 1037 | } 1038 | }, 1039 | "iconv-lite": { 1040 | "version": "0.4.24", 1041 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 1042 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 1043 | "dev": true, 1044 | "requires": { 1045 | "safer-buffer": ">= 2.1.2 < 3" 1046 | } 1047 | }, 1048 | "imurmurhash": { 1049 | "version": "0.1.4", 1050 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 1051 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 1052 | "dev": true 1053 | }, 1054 | "indent-string": { 1055 | "version": "4.0.0", 1056 | "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", 1057 | "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", 1058 | "dev": true 1059 | }, 1060 | "inflight": { 1061 | "version": "1.0.6", 1062 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1063 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1064 | "dev": true, 1065 | "requires": { 1066 | "once": "^1.3.0", 1067 | "wrappy": "1" 1068 | } 1069 | }, 1070 | "inherits": { 1071 | "version": "2.0.3", 1072 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 1073 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 1074 | "dev": true 1075 | }, 1076 | "ipaddr.js": { 1077 | "version": "1.9.0", 1078 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", 1079 | "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==", 1080 | "dev": true 1081 | }, 1082 | "is-binary-path": { 1083 | "version": "2.1.0", 1084 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1085 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1086 | "dev": true, 1087 | "requires": { 1088 | "binary-extensions": "^2.0.0" 1089 | } 1090 | }, 1091 | "is-core-module": { 1092 | "version": "2.0.0", 1093 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.0.0.tgz", 1094 | "integrity": "sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw==", 1095 | "dev": true, 1096 | "requires": { 1097 | "has": "^1.0.3" 1098 | } 1099 | }, 1100 | "is-extglob": { 1101 | "version": "2.1.1", 1102 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1103 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 1104 | "dev": true 1105 | }, 1106 | "is-fullwidth-code-point": { 1107 | "version": "2.0.0", 1108 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 1109 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 1110 | "dev": true 1111 | }, 1112 | "is-glob": { 1113 | "version": "4.0.1", 1114 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 1115 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 1116 | "dev": true, 1117 | "requires": { 1118 | "is-extglob": "^2.1.1" 1119 | } 1120 | }, 1121 | "is-number": { 1122 | "version": "7.0.0", 1123 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1124 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1125 | "dev": true 1126 | }, 1127 | "is-plain-obj": { 1128 | "version": "2.1.0", 1129 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 1130 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 1131 | "dev": true 1132 | }, 1133 | "is-stream": { 1134 | "version": "2.0.0", 1135 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", 1136 | "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", 1137 | "dev": true 1138 | }, 1139 | "is-typedarray": { 1140 | "version": "1.0.0", 1141 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 1142 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", 1143 | "dev": true 1144 | }, 1145 | "is-windows": { 1146 | "version": "1.0.2", 1147 | "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", 1148 | "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", 1149 | "dev": true 1150 | }, 1151 | "isexe": { 1152 | "version": "2.0.0", 1153 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1154 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 1155 | "dev": true 1156 | }, 1157 | "istanbul-lib-coverage": { 1158 | "version": "3.0.0", 1159 | "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", 1160 | "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", 1161 | "dev": true 1162 | }, 1163 | "istanbul-lib-hook": { 1164 | "version": "3.0.0", 1165 | "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", 1166 | "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", 1167 | "dev": true, 1168 | "requires": { 1169 | "append-transform": "^2.0.0" 1170 | } 1171 | }, 1172 | "istanbul-lib-instrument": { 1173 | "version": "4.0.3", 1174 | "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", 1175 | "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", 1176 | "dev": true, 1177 | "requires": { 1178 | "@babel/core": "^7.7.5", 1179 | "@istanbuljs/schema": "^0.1.2", 1180 | "istanbul-lib-coverage": "^3.0.0", 1181 | "semver": "^6.3.0" 1182 | } 1183 | }, 1184 | "istanbul-lib-processinfo": { 1185 | "version": "2.0.2", 1186 | "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", 1187 | "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", 1188 | "dev": true, 1189 | "requires": { 1190 | "archy": "^1.0.0", 1191 | "cross-spawn": "^7.0.0", 1192 | "istanbul-lib-coverage": "^3.0.0-alpha.1", 1193 | "make-dir": "^3.0.0", 1194 | "p-map": "^3.0.0", 1195 | "rimraf": "^3.0.0", 1196 | "uuid": "^3.3.3" 1197 | } 1198 | }, 1199 | "istanbul-lib-report": { 1200 | "version": "3.0.0", 1201 | "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", 1202 | "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", 1203 | "dev": true, 1204 | "requires": { 1205 | "istanbul-lib-coverage": "^3.0.0", 1206 | "make-dir": "^3.0.0", 1207 | "supports-color": "^7.1.0" 1208 | } 1209 | }, 1210 | "istanbul-lib-source-maps": { 1211 | "version": "4.0.0", 1212 | "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", 1213 | "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", 1214 | "dev": true, 1215 | "requires": { 1216 | "debug": "^4.1.1", 1217 | "istanbul-lib-coverage": "^3.0.0", 1218 | "source-map": "^0.6.1" 1219 | }, 1220 | "dependencies": { 1221 | "debug": { 1222 | "version": "4.2.0", 1223 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", 1224 | "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", 1225 | "dev": true, 1226 | "requires": { 1227 | "ms": "2.1.2" 1228 | } 1229 | }, 1230 | "ms": { 1231 | "version": "2.1.2", 1232 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1233 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1234 | "dev": true 1235 | }, 1236 | "source-map": { 1237 | "version": "0.6.1", 1238 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1239 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1240 | "dev": true 1241 | } 1242 | } 1243 | }, 1244 | "istanbul-reports": { 1245 | "version": "3.0.2", 1246 | "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", 1247 | "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", 1248 | "dev": true, 1249 | "requires": { 1250 | "html-escaper": "^2.0.0", 1251 | "istanbul-lib-report": "^3.0.0" 1252 | } 1253 | }, 1254 | "js-tokens": { 1255 | "version": "4.0.0", 1256 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 1257 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 1258 | "dev": true 1259 | }, 1260 | "js-yaml": { 1261 | "version": "3.14.0", 1262 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", 1263 | "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", 1264 | "dev": true, 1265 | "requires": { 1266 | "argparse": "^1.0.7", 1267 | "esprima": "^4.0.0" 1268 | } 1269 | }, 1270 | "jsesc": { 1271 | "version": "2.5.2", 1272 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", 1273 | "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", 1274 | "dev": true 1275 | }, 1276 | "json5": { 1277 | "version": "2.1.3", 1278 | "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", 1279 | "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", 1280 | "dev": true, 1281 | "requires": { 1282 | "minimist": "^1.2.5" 1283 | } 1284 | }, 1285 | "locate-path": { 1286 | "version": "6.0.0", 1287 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1288 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1289 | "dev": true, 1290 | "requires": { 1291 | "p-locate": "^5.0.0" 1292 | } 1293 | }, 1294 | "lodash": { 1295 | "version": "4.17.20", 1296 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", 1297 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", 1298 | "dev": true 1299 | }, 1300 | "lodash.flattendeep": { 1301 | "version": "4.4.0", 1302 | "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", 1303 | "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", 1304 | "dev": true 1305 | }, 1306 | "log-symbols": { 1307 | "version": "4.0.0", 1308 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", 1309 | "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", 1310 | "dev": true, 1311 | "requires": { 1312 | "chalk": "^4.0.0" 1313 | }, 1314 | "dependencies": { 1315 | "ansi-styles": { 1316 | "version": "4.3.0", 1317 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1318 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1319 | "dev": true, 1320 | "requires": { 1321 | "color-convert": "^2.0.1" 1322 | } 1323 | }, 1324 | "chalk": { 1325 | "version": "4.1.0", 1326 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", 1327 | "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", 1328 | "dev": true, 1329 | "requires": { 1330 | "ansi-styles": "^4.1.0", 1331 | "supports-color": "^7.1.0" 1332 | } 1333 | }, 1334 | "color-convert": { 1335 | "version": "2.0.1", 1336 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1337 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1338 | "dev": true, 1339 | "requires": { 1340 | "color-name": "~1.1.4" 1341 | } 1342 | }, 1343 | "color-name": { 1344 | "version": "1.1.4", 1345 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1346 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1347 | "dev": true 1348 | } 1349 | } 1350 | }, 1351 | "make-dir": { 1352 | "version": "3.1.0", 1353 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", 1354 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", 1355 | "dev": true, 1356 | "requires": { 1357 | "semver": "^6.0.0" 1358 | } 1359 | }, 1360 | "media-typer": { 1361 | "version": "0.3.0", 1362 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1363 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", 1364 | "dev": true 1365 | }, 1366 | "merge-descriptors": { 1367 | "version": "1.0.1", 1368 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 1369 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", 1370 | "dev": true 1371 | }, 1372 | "methods": { 1373 | "version": "1.1.2", 1374 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1375 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", 1376 | "dev": true 1377 | }, 1378 | "mime": { 1379 | "version": "1.6.0", 1380 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1381 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 1382 | "dev": true 1383 | }, 1384 | "mime-db": { 1385 | "version": "1.40.0", 1386 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", 1387 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", 1388 | "dev": true 1389 | }, 1390 | "mime-types": { 1391 | "version": "2.1.24", 1392 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", 1393 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", 1394 | "dev": true, 1395 | "requires": { 1396 | "mime-db": "1.40.0" 1397 | } 1398 | }, 1399 | "minimatch": { 1400 | "version": "3.0.4", 1401 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1402 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1403 | "dev": true, 1404 | "requires": { 1405 | "brace-expansion": "^1.1.7" 1406 | } 1407 | }, 1408 | "minimist": { 1409 | "version": "1.2.5", 1410 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 1411 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 1412 | "dev": true 1413 | }, 1414 | "mocha": { 1415 | "version": "8.2.0", 1416 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.0.tgz", 1417 | "integrity": "sha512-lEWEMq2LMfNJMKeuEwb5UELi+OgFDollXaytR5ggQcHpzG3NP/R7rvixAvF+9/lLsTWhWG+4yD2M70GsM06nxw==", 1418 | "dev": true, 1419 | "requires": { 1420 | "@ungap/promise-all-settled": "1.1.2", 1421 | "ansi-colors": "4.1.1", 1422 | "browser-stdout": "1.3.1", 1423 | "chokidar": "3.4.3", 1424 | "debug": "4.2.0", 1425 | "diff": "4.0.2", 1426 | "escape-string-regexp": "4.0.0", 1427 | "find-up": "5.0.0", 1428 | "glob": "7.1.6", 1429 | "growl": "1.10.5", 1430 | "he": "1.2.0", 1431 | "js-yaml": "3.14.0", 1432 | "log-symbols": "4.0.0", 1433 | "minimatch": "3.0.4", 1434 | "ms": "2.1.2", 1435 | "nanoid": "3.1.12", 1436 | "serialize-javascript": "5.0.1", 1437 | "strip-json-comments": "3.1.1", 1438 | "supports-color": "7.2.0", 1439 | "which": "2.0.2", 1440 | "wide-align": "1.1.3", 1441 | "workerpool": "6.0.2", 1442 | "yargs": "13.3.2", 1443 | "yargs-parser": "13.1.2", 1444 | "yargs-unparser": "2.0.0" 1445 | }, 1446 | "dependencies": { 1447 | "debug": { 1448 | "version": "4.2.0", 1449 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", 1450 | "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", 1451 | "dev": true, 1452 | "requires": { 1453 | "ms": "2.1.2" 1454 | } 1455 | }, 1456 | "escape-string-regexp": { 1457 | "version": "4.0.0", 1458 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1459 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1460 | "dev": true 1461 | }, 1462 | "glob": { 1463 | "version": "7.1.6", 1464 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 1465 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 1466 | "dev": true, 1467 | "requires": { 1468 | "fs.realpath": "^1.0.0", 1469 | "inflight": "^1.0.4", 1470 | "inherits": "2", 1471 | "minimatch": "^3.0.4", 1472 | "once": "^1.3.0", 1473 | "path-is-absolute": "^1.0.0" 1474 | } 1475 | }, 1476 | "js-yaml": { 1477 | "version": "3.14.0", 1478 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", 1479 | "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", 1480 | "dev": true, 1481 | "requires": { 1482 | "argparse": "^1.0.7", 1483 | "esprima": "^4.0.0" 1484 | } 1485 | }, 1486 | "ms": { 1487 | "version": "2.1.2", 1488 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1489 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1490 | "dev": true 1491 | } 1492 | } 1493 | }, 1494 | "ms": { 1495 | "version": "2.0.0", 1496 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1497 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 1498 | "dev": true 1499 | }, 1500 | "nanoid": { 1501 | "version": "3.1.12", 1502 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", 1503 | "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", 1504 | "dev": true 1505 | }, 1506 | "negotiator": { 1507 | "version": "0.6.2", 1508 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 1509 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", 1510 | "dev": true 1511 | }, 1512 | "node-preload": { 1513 | "version": "0.2.1", 1514 | "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", 1515 | "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", 1516 | "dev": true, 1517 | "requires": { 1518 | "process-on-spawn": "^1.0.0" 1519 | } 1520 | }, 1521 | "normalize-path": { 1522 | "version": "3.0.0", 1523 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1524 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1525 | "dev": true 1526 | }, 1527 | "nyc": { 1528 | "version": "15.1.0", 1529 | "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", 1530 | "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", 1531 | "dev": true, 1532 | "requires": { 1533 | "@istanbuljs/load-nyc-config": "^1.0.0", 1534 | "@istanbuljs/schema": "^0.1.2", 1535 | "caching-transform": "^4.0.0", 1536 | "convert-source-map": "^1.7.0", 1537 | "decamelize": "^1.2.0", 1538 | "find-cache-dir": "^3.2.0", 1539 | "find-up": "^4.1.0", 1540 | "foreground-child": "^2.0.0", 1541 | "get-package-type": "^0.1.0", 1542 | "glob": "^7.1.6", 1543 | "istanbul-lib-coverage": "^3.0.0", 1544 | "istanbul-lib-hook": "^3.0.0", 1545 | "istanbul-lib-instrument": "^4.0.0", 1546 | "istanbul-lib-processinfo": "^2.0.2", 1547 | "istanbul-lib-report": "^3.0.0", 1548 | "istanbul-lib-source-maps": "^4.0.0", 1549 | "istanbul-reports": "^3.0.2", 1550 | "make-dir": "^3.0.0", 1551 | "node-preload": "^0.2.1", 1552 | "p-map": "^3.0.0", 1553 | "process-on-spawn": "^1.0.0", 1554 | "resolve-from": "^5.0.0", 1555 | "rimraf": "^3.0.0", 1556 | "signal-exit": "^3.0.2", 1557 | "spawn-wrap": "^2.0.0", 1558 | "test-exclude": "^6.0.0", 1559 | "yargs": "^15.0.2" 1560 | }, 1561 | "dependencies": { 1562 | "ansi-regex": { 1563 | "version": "5.0.0", 1564 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 1565 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", 1566 | "dev": true 1567 | }, 1568 | "ansi-styles": { 1569 | "version": "4.3.0", 1570 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1571 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1572 | "dev": true, 1573 | "requires": { 1574 | "color-convert": "^2.0.1" 1575 | } 1576 | }, 1577 | "cliui": { 1578 | "version": "6.0.0", 1579 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", 1580 | "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", 1581 | "dev": true, 1582 | "requires": { 1583 | "string-width": "^4.2.0", 1584 | "strip-ansi": "^6.0.0", 1585 | "wrap-ansi": "^6.2.0" 1586 | } 1587 | }, 1588 | "color-convert": { 1589 | "version": "2.0.1", 1590 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1591 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1592 | "dev": true, 1593 | "requires": { 1594 | "color-name": "~1.1.4" 1595 | } 1596 | }, 1597 | "color-name": { 1598 | "version": "1.1.4", 1599 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1600 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1601 | "dev": true 1602 | }, 1603 | "emoji-regex": { 1604 | "version": "8.0.0", 1605 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1606 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1607 | "dev": true 1608 | }, 1609 | "find-up": { 1610 | "version": "4.1.0", 1611 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", 1612 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", 1613 | "dev": true, 1614 | "requires": { 1615 | "locate-path": "^5.0.0", 1616 | "path-exists": "^4.0.0" 1617 | } 1618 | }, 1619 | "is-fullwidth-code-point": { 1620 | "version": "3.0.0", 1621 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1622 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1623 | "dev": true 1624 | }, 1625 | "locate-path": { 1626 | "version": "5.0.0", 1627 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", 1628 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", 1629 | "dev": true, 1630 | "requires": { 1631 | "p-locate": "^4.1.0" 1632 | } 1633 | }, 1634 | "p-locate": { 1635 | "version": "4.1.0", 1636 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", 1637 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", 1638 | "dev": true, 1639 | "requires": { 1640 | "p-limit": "^2.2.0" 1641 | } 1642 | }, 1643 | "string-width": { 1644 | "version": "4.2.0", 1645 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", 1646 | "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", 1647 | "dev": true, 1648 | "requires": { 1649 | "emoji-regex": "^8.0.0", 1650 | "is-fullwidth-code-point": "^3.0.0", 1651 | "strip-ansi": "^6.0.0" 1652 | } 1653 | }, 1654 | "strip-ansi": { 1655 | "version": "6.0.0", 1656 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 1657 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 1658 | "dev": true, 1659 | "requires": { 1660 | "ansi-regex": "^5.0.0" 1661 | } 1662 | }, 1663 | "wrap-ansi": { 1664 | "version": "6.2.0", 1665 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", 1666 | "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", 1667 | "dev": true, 1668 | "requires": { 1669 | "ansi-styles": "^4.0.0", 1670 | "string-width": "^4.1.0", 1671 | "strip-ansi": "^6.0.0" 1672 | } 1673 | }, 1674 | "yargs": { 1675 | "version": "15.4.1", 1676 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", 1677 | "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", 1678 | "dev": true, 1679 | "requires": { 1680 | "cliui": "^6.0.0", 1681 | "decamelize": "^1.2.0", 1682 | "find-up": "^4.1.0", 1683 | "get-caller-file": "^2.0.1", 1684 | "require-directory": "^2.1.1", 1685 | "require-main-filename": "^2.0.0", 1686 | "set-blocking": "^2.0.0", 1687 | "string-width": "^4.2.0", 1688 | "which-module": "^2.0.0", 1689 | "y18n": "^4.0.0", 1690 | "yargs-parser": "^18.1.2" 1691 | } 1692 | }, 1693 | "yargs-parser": { 1694 | "version": "18.1.3", 1695 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", 1696 | "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", 1697 | "dev": true, 1698 | "requires": { 1699 | "camelcase": "^5.0.0", 1700 | "decamelize": "^1.2.0" 1701 | } 1702 | } 1703 | } 1704 | }, 1705 | "on-finished": { 1706 | "version": "2.3.0", 1707 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 1708 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 1709 | "dev": true, 1710 | "requires": { 1711 | "ee-first": "1.1.1" 1712 | } 1713 | }, 1714 | "once": { 1715 | "version": "1.4.0", 1716 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1717 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1718 | "dev": true, 1719 | "requires": { 1720 | "wrappy": "1" 1721 | } 1722 | }, 1723 | "p-limit": { 1724 | "version": "2.2.2", 1725 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", 1726 | "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", 1727 | "dev": true, 1728 | "requires": { 1729 | "p-try": "^2.0.0" 1730 | } 1731 | }, 1732 | "p-locate": { 1733 | "version": "5.0.0", 1734 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1735 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1736 | "dev": true, 1737 | "requires": { 1738 | "p-limit": "^3.0.2" 1739 | }, 1740 | "dependencies": { 1741 | "p-limit": { 1742 | "version": "3.0.2", 1743 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", 1744 | "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", 1745 | "dev": true, 1746 | "requires": { 1747 | "p-try": "^2.0.0" 1748 | } 1749 | } 1750 | } 1751 | }, 1752 | "p-map": { 1753 | "version": "3.0.0", 1754 | "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", 1755 | "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", 1756 | "dev": true, 1757 | "requires": { 1758 | "aggregate-error": "^3.0.0" 1759 | } 1760 | }, 1761 | "p-try": { 1762 | "version": "2.2.0", 1763 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 1764 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", 1765 | "dev": true 1766 | }, 1767 | "package-hash": { 1768 | "version": "4.0.0", 1769 | "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", 1770 | "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", 1771 | "dev": true, 1772 | "requires": { 1773 | "graceful-fs": "^4.1.15", 1774 | "hasha": "^5.0.0", 1775 | "lodash.flattendeep": "^4.4.0", 1776 | "release-zalgo": "^1.0.0" 1777 | } 1778 | }, 1779 | "parseurl": { 1780 | "version": "1.3.3", 1781 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1782 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 1783 | "dev": true 1784 | }, 1785 | "path-exists": { 1786 | "version": "4.0.0", 1787 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1788 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1789 | "dev": true 1790 | }, 1791 | "path-is-absolute": { 1792 | "version": "1.0.1", 1793 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1794 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1795 | "dev": true 1796 | }, 1797 | "path-key": { 1798 | "version": "3.1.1", 1799 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1800 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1801 | "dev": true 1802 | }, 1803 | "path-parse": { 1804 | "version": "1.0.6", 1805 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 1806 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 1807 | "dev": true 1808 | }, 1809 | "path-to-regexp": { 1810 | "version": "0.1.7", 1811 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1812 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", 1813 | "dev": true 1814 | }, 1815 | "picomatch": { 1816 | "version": "2.2.2", 1817 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", 1818 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", 1819 | "dev": true 1820 | }, 1821 | "pkg-dir": { 1822 | "version": "4.2.0", 1823 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", 1824 | "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", 1825 | "dev": true, 1826 | "requires": { 1827 | "find-up": "^4.0.0" 1828 | }, 1829 | "dependencies": { 1830 | "find-up": { 1831 | "version": "4.1.0", 1832 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", 1833 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", 1834 | "dev": true, 1835 | "requires": { 1836 | "locate-path": "^5.0.0", 1837 | "path-exists": "^4.0.0" 1838 | } 1839 | }, 1840 | "locate-path": { 1841 | "version": "5.0.0", 1842 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", 1843 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", 1844 | "dev": true, 1845 | "requires": { 1846 | "p-locate": "^4.1.0" 1847 | } 1848 | }, 1849 | "p-locate": { 1850 | "version": "4.1.0", 1851 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", 1852 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", 1853 | "dev": true, 1854 | "requires": { 1855 | "p-limit": "^2.2.0" 1856 | } 1857 | } 1858 | } 1859 | }, 1860 | "process-on-spawn": { 1861 | "version": "1.0.0", 1862 | "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", 1863 | "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", 1864 | "dev": true, 1865 | "requires": { 1866 | "fromentries": "^1.2.0" 1867 | } 1868 | }, 1869 | "proxy-addr": { 1870 | "version": "2.0.5", 1871 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", 1872 | "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", 1873 | "dev": true, 1874 | "requires": { 1875 | "forwarded": "~0.1.2", 1876 | "ipaddr.js": "1.9.0" 1877 | } 1878 | }, 1879 | "qs": { 1880 | "version": "6.7.0", 1881 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 1882 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", 1883 | "dev": true 1884 | }, 1885 | "randombytes": { 1886 | "version": "2.1.0", 1887 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 1888 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 1889 | "dev": true, 1890 | "requires": { 1891 | "safe-buffer": "^5.1.0" 1892 | } 1893 | }, 1894 | "range-parser": { 1895 | "version": "1.2.1", 1896 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1897 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 1898 | "dev": true 1899 | }, 1900 | "raw-body": { 1901 | "version": "2.4.0", 1902 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 1903 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 1904 | "dev": true, 1905 | "requires": { 1906 | "bytes": "3.1.0", 1907 | "http-errors": "1.7.2", 1908 | "iconv-lite": "0.4.24", 1909 | "unpipe": "1.0.0" 1910 | } 1911 | }, 1912 | "readable-stream": { 1913 | "version": "3.6.0", 1914 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 1915 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 1916 | "dev": true, 1917 | "requires": { 1918 | "inherits": "^2.0.3", 1919 | "string_decoder": "^1.1.1", 1920 | "util-deprecate": "^1.0.1" 1921 | } 1922 | }, 1923 | "readdirp": { 1924 | "version": "3.5.0", 1925 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", 1926 | "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", 1927 | "dev": true, 1928 | "requires": { 1929 | "picomatch": "^2.2.1" 1930 | } 1931 | }, 1932 | "release-zalgo": { 1933 | "version": "1.0.0", 1934 | "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", 1935 | "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", 1936 | "dev": true, 1937 | "requires": { 1938 | "es6-error": "^4.0.1" 1939 | } 1940 | }, 1941 | "require-directory": { 1942 | "version": "2.1.1", 1943 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1944 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 1945 | "dev": true 1946 | }, 1947 | "require-main-filename": { 1948 | "version": "2.0.0", 1949 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 1950 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", 1951 | "dev": true 1952 | }, 1953 | "resolve": { 1954 | "version": "1.18.1", 1955 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", 1956 | "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", 1957 | "dev": true, 1958 | "requires": { 1959 | "is-core-module": "^2.0.0", 1960 | "path-parse": "^1.0.6" 1961 | } 1962 | }, 1963 | "resolve-from": { 1964 | "version": "5.0.0", 1965 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", 1966 | "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", 1967 | "dev": true 1968 | }, 1969 | "rimraf": { 1970 | "version": "3.0.2", 1971 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1972 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1973 | "dev": true, 1974 | "requires": { 1975 | "glob": "^7.1.3" 1976 | } 1977 | }, 1978 | "safe-buffer": { 1979 | "version": "5.1.2", 1980 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1981 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 1982 | "dev": true 1983 | }, 1984 | "safer-buffer": { 1985 | "version": "2.1.2", 1986 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1987 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 1988 | "dev": true 1989 | }, 1990 | "semver": { 1991 | "version": "6.3.0", 1992 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 1993 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 1994 | "dev": true 1995 | }, 1996 | "send": { 1997 | "version": "0.17.1", 1998 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 1999 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 2000 | "dev": true, 2001 | "requires": { 2002 | "debug": "2.6.9", 2003 | "depd": "~1.1.2", 2004 | "destroy": "~1.0.4", 2005 | "encodeurl": "~1.0.2", 2006 | "escape-html": "~1.0.3", 2007 | "etag": "~1.8.1", 2008 | "fresh": "0.5.2", 2009 | "http-errors": "~1.7.2", 2010 | "mime": "1.6.0", 2011 | "ms": "2.1.1", 2012 | "on-finished": "~2.3.0", 2013 | "range-parser": "~1.2.1", 2014 | "statuses": "~1.5.0" 2015 | }, 2016 | "dependencies": { 2017 | "ms": { 2018 | "version": "2.1.1", 2019 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 2020 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", 2021 | "dev": true 2022 | } 2023 | } 2024 | }, 2025 | "serialize-javascript": { 2026 | "version": "5.0.1", 2027 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", 2028 | "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", 2029 | "dev": true, 2030 | "requires": { 2031 | "randombytes": "^2.1.0" 2032 | } 2033 | }, 2034 | "serve-static": { 2035 | "version": "1.14.1", 2036 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 2037 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 2038 | "dev": true, 2039 | "requires": { 2040 | "encodeurl": "~1.0.2", 2041 | "escape-html": "~1.0.3", 2042 | "parseurl": "~1.3.3", 2043 | "send": "0.17.1" 2044 | } 2045 | }, 2046 | "set-blocking": { 2047 | "version": "2.0.0", 2048 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 2049 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", 2050 | "dev": true 2051 | }, 2052 | "setprototypeof": { 2053 | "version": "1.1.1", 2054 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 2055 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", 2056 | "dev": true 2057 | }, 2058 | "shebang-command": { 2059 | "version": "2.0.0", 2060 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 2061 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 2062 | "dev": true, 2063 | "requires": { 2064 | "shebang-regex": "^3.0.0" 2065 | } 2066 | }, 2067 | "shebang-regex": { 2068 | "version": "3.0.0", 2069 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 2070 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 2071 | "dev": true 2072 | }, 2073 | "signal-exit": { 2074 | "version": "3.0.3", 2075 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", 2076 | "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", 2077 | "dev": true 2078 | }, 2079 | "source-map": { 2080 | "version": "0.5.7", 2081 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 2082 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 2083 | "dev": true 2084 | }, 2085 | "spawn-wrap": { 2086 | "version": "2.0.0", 2087 | "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", 2088 | "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", 2089 | "dev": true, 2090 | "requires": { 2091 | "foreground-child": "^2.0.0", 2092 | "is-windows": "^1.0.2", 2093 | "make-dir": "^3.0.0", 2094 | "rimraf": "^3.0.0", 2095 | "signal-exit": "^3.0.2", 2096 | "which": "^2.0.1" 2097 | } 2098 | }, 2099 | "sprintf-js": { 2100 | "version": "1.0.3", 2101 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 2102 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 2103 | "dev": true 2104 | }, 2105 | "statuses": { 2106 | "version": "1.5.0", 2107 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 2108 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", 2109 | "dev": true 2110 | }, 2111 | "string-width": { 2112 | "version": "2.1.1", 2113 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 2114 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 2115 | "dev": true, 2116 | "requires": { 2117 | "is-fullwidth-code-point": "^2.0.0", 2118 | "strip-ansi": "^4.0.0" 2119 | } 2120 | }, 2121 | "string_decoder": { 2122 | "version": "1.3.0", 2123 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 2124 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 2125 | "dev": true, 2126 | "requires": { 2127 | "safe-buffer": "~5.2.0" 2128 | }, 2129 | "dependencies": { 2130 | "safe-buffer": { 2131 | "version": "5.2.1", 2132 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 2133 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 2134 | "dev": true 2135 | } 2136 | } 2137 | }, 2138 | "strip-ansi": { 2139 | "version": "4.0.0", 2140 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 2141 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 2142 | "dev": true, 2143 | "requires": { 2144 | "ansi-regex": "^3.0.0" 2145 | } 2146 | }, 2147 | "strip-bom": { 2148 | "version": "4.0.0", 2149 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", 2150 | "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", 2151 | "dev": true 2152 | }, 2153 | "strip-json-comments": { 2154 | "version": "3.1.1", 2155 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 2156 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 2157 | "dev": true 2158 | }, 2159 | "superagent": { 2160 | "version": "6.1.0", 2161 | "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", 2162 | "integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==", 2163 | "dev": true, 2164 | "requires": { 2165 | "component-emitter": "^1.3.0", 2166 | "cookiejar": "^2.1.2", 2167 | "debug": "^4.1.1", 2168 | "fast-safe-stringify": "^2.0.7", 2169 | "form-data": "^3.0.0", 2170 | "formidable": "^1.2.2", 2171 | "methods": "^1.1.2", 2172 | "mime": "^2.4.6", 2173 | "qs": "^6.9.4", 2174 | "readable-stream": "^3.6.0", 2175 | "semver": "^7.3.2" 2176 | }, 2177 | "dependencies": { 2178 | "debug": { 2179 | "version": "4.2.0", 2180 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", 2181 | "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", 2182 | "dev": true, 2183 | "requires": { 2184 | "ms": "2.1.2" 2185 | } 2186 | }, 2187 | "mime": { 2188 | "version": "2.4.6", 2189 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", 2190 | "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", 2191 | "dev": true 2192 | }, 2193 | "ms": { 2194 | "version": "2.1.2", 2195 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 2196 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 2197 | "dev": true 2198 | }, 2199 | "qs": { 2200 | "version": "6.9.4", 2201 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", 2202 | "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", 2203 | "dev": true 2204 | }, 2205 | "semver": { 2206 | "version": "7.3.2", 2207 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", 2208 | "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", 2209 | "dev": true 2210 | } 2211 | } 2212 | }, 2213 | "supertest": { 2214 | "version": "5.0.0", 2215 | "resolved": "https://registry.npmjs.org/supertest/-/supertest-5.0.0.tgz", 2216 | "integrity": "sha512-2JAWpPrUOZF4hHH5ZTCN2xjKXvJS3AEwPNXl0HUseHsfcXFvMy9kcsufIHCNAmQ5hlGCvgeAqaR5PBEouN3hlQ==", 2217 | "dev": true, 2218 | "requires": { 2219 | "methods": "1.1.2", 2220 | "superagent": "6.1.0" 2221 | } 2222 | }, 2223 | "supports-color": { 2224 | "version": "7.2.0", 2225 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 2226 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 2227 | "dev": true, 2228 | "requires": { 2229 | "has-flag": "^4.0.0" 2230 | }, 2231 | "dependencies": { 2232 | "has-flag": { 2233 | "version": "4.0.0", 2234 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 2235 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 2236 | "dev": true 2237 | } 2238 | } 2239 | }, 2240 | "test-exclude": { 2241 | "version": "6.0.0", 2242 | "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", 2243 | "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", 2244 | "dev": true, 2245 | "requires": { 2246 | "@istanbuljs/schema": "^0.1.2", 2247 | "glob": "^7.1.4", 2248 | "minimatch": "^3.0.4" 2249 | } 2250 | }, 2251 | "to-fast-properties": { 2252 | "version": "2.0.0", 2253 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", 2254 | "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", 2255 | "dev": true 2256 | }, 2257 | "to-regex-range": { 2258 | "version": "5.0.1", 2259 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 2260 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 2261 | "dev": true, 2262 | "requires": { 2263 | "is-number": "^7.0.0" 2264 | } 2265 | }, 2266 | "toidentifier": { 2267 | "version": "1.0.0", 2268 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 2269 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", 2270 | "dev": true 2271 | }, 2272 | "type-fest": { 2273 | "version": "0.8.1", 2274 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", 2275 | "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", 2276 | "dev": true 2277 | }, 2278 | "type-is": { 2279 | "version": "1.6.18", 2280 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 2281 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 2282 | "dev": true, 2283 | "requires": { 2284 | "media-typer": "0.3.0", 2285 | "mime-types": "~2.1.24" 2286 | } 2287 | }, 2288 | "typedarray-to-buffer": { 2289 | "version": "3.1.5", 2290 | "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", 2291 | "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", 2292 | "dev": true, 2293 | "requires": { 2294 | "is-typedarray": "^1.0.0" 2295 | } 2296 | }, 2297 | "unpipe": { 2298 | "version": "1.0.0", 2299 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 2300 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", 2301 | "dev": true 2302 | }, 2303 | "util-deprecate": { 2304 | "version": "1.0.2", 2305 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 2306 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 2307 | "dev": true 2308 | }, 2309 | "utils-merge": { 2310 | "version": "1.0.1", 2311 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 2312 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", 2313 | "dev": true 2314 | }, 2315 | "uuid": { 2316 | "version": "3.4.0", 2317 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 2318 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", 2319 | "dev": true 2320 | }, 2321 | "vary": { 2322 | "version": "1.1.2", 2323 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 2324 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", 2325 | "dev": true 2326 | }, 2327 | "which": { 2328 | "version": "2.0.2", 2329 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 2330 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 2331 | "dev": true, 2332 | "requires": { 2333 | "isexe": "^2.0.0" 2334 | } 2335 | }, 2336 | "which-module": { 2337 | "version": "2.0.0", 2338 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 2339 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", 2340 | "dev": true 2341 | }, 2342 | "wide-align": { 2343 | "version": "1.1.3", 2344 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 2345 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 2346 | "dev": true, 2347 | "requires": { 2348 | "string-width": "^1.0.2 || 2" 2349 | } 2350 | }, 2351 | "workerpool": { 2352 | "version": "6.0.2", 2353 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", 2354 | "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", 2355 | "dev": true 2356 | }, 2357 | "wrap-ansi": { 2358 | "version": "5.1.0", 2359 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", 2360 | "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", 2361 | "dev": true, 2362 | "requires": { 2363 | "ansi-styles": "^3.2.0", 2364 | "string-width": "^3.0.0", 2365 | "strip-ansi": "^5.0.0" 2366 | }, 2367 | "dependencies": { 2368 | "ansi-regex": { 2369 | "version": "4.1.0", 2370 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 2371 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 2372 | "dev": true 2373 | }, 2374 | "string-width": { 2375 | "version": "3.1.0", 2376 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 2377 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 2378 | "dev": true, 2379 | "requires": { 2380 | "emoji-regex": "^7.0.1", 2381 | "is-fullwidth-code-point": "^2.0.0", 2382 | "strip-ansi": "^5.1.0" 2383 | } 2384 | }, 2385 | "strip-ansi": { 2386 | "version": "5.2.0", 2387 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 2388 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 2389 | "dev": true, 2390 | "requires": { 2391 | "ansi-regex": "^4.1.0" 2392 | } 2393 | } 2394 | } 2395 | }, 2396 | "wrappy": { 2397 | "version": "1.0.2", 2398 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2399 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 2400 | "dev": true 2401 | }, 2402 | "write-file-atomic": { 2403 | "version": "3.0.3", 2404 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", 2405 | "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", 2406 | "dev": true, 2407 | "requires": { 2408 | "imurmurhash": "^0.1.4", 2409 | "is-typedarray": "^1.0.0", 2410 | "signal-exit": "^3.0.2", 2411 | "typedarray-to-buffer": "^3.1.5" 2412 | } 2413 | }, 2414 | "y18n": { 2415 | "version": "4.0.0", 2416 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", 2417 | "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", 2418 | "dev": true 2419 | }, 2420 | "yargs": { 2421 | "version": "13.3.2", 2422 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", 2423 | "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", 2424 | "dev": true, 2425 | "requires": { 2426 | "cliui": "^5.0.0", 2427 | "find-up": "^3.0.0", 2428 | "get-caller-file": "^2.0.1", 2429 | "require-directory": "^2.1.1", 2430 | "require-main-filename": "^2.0.0", 2431 | "set-blocking": "^2.0.0", 2432 | "string-width": "^3.0.0", 2433 | "which-module": "^2.0.0", 2434 | "y18n": "^4.0.0", 2435 | "yargs-parser": "^13.1.2" 2436 | }, 2437 | "dependencies": { 2438 | "ansi-regex": { 2439 | "version": "4.1.0", 2440 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 2441 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 2442 | "dev": true 2443 | }, 2444 | "find-up": { 2445 | "version": "3.0.0", 2446 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 2447 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 2448 | "dev": true, 2449 | "requires": { 2450 | "locate-path": "^3.0.0" 2451 | } 2452 | }, 2453 | "locate-path": { 2454 | "version": "3.0.0", 2455 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 2456 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 2457 | "dev": true, 2458 | "requires": { 2459 | "p-locate": "^3.0.0", 2460 | "path-exists": "^3.0.0" 2461 | } 2462 | }, 2463 | "p-locate": { 2464 | "version": "3.0.0", 2465 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 2466 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 2467 | "dev": true, 2468 | "requires": { 2469 | "p-limit": "^2.0.0" 2470 | } 2471 | }, 2472 | "path-exists": { 2473 | "version": "3.0.0", 2474 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 2475 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", 2476 | "dev": true 2477 | }, 2478 | "string-width": { 2479 | "version": "3.1.0", 2480 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 2481 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 2482 | "dev": true, 2483 | "requires": { 2484 | "emoji-regex": "^7.0.1", 2485 | "is-fullwidth-code-point": "^2.0.0", 2486 | "strip-ansi": "^5.1.0" 2487 | } 2488 | }, 2489 | "strip-ansi": { 2490 | "version": "5.2.0", 2491 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 2492 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 2493 | "dev": true, 2494 | "requires": { 2495 | "ansi-regex": "^4.1.0" 2496 | } 2497 | } 2498 | } 2499 | }, 2500 | "yargs-parser": { 2501 | "version": "13.1.2", 2502 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", 2503 | "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", 2504 | "dev": true, 2505 | "requires": { 2506 | "camelcase": "^5.0.0", 2507 | "decamelize": "^1.2.0" 2508 | } 2509 | }, 2510 | "yargs-unparser": { 2511 | "version": "2.0.0", 2512 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 2513 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 2514 | "dev": true, 2515 | "requires": { 2516 | "camelcase": "^6.0.0", 2517 | "decamelize": "^4.0.0", 2518 | "flat": "^5.0.2", 2519 | "is-plain-obj": "^2.1.0" 2520 | }, 2521 | "dependencies": { 2522 | "camelcase": { 2523 | "version": "6.1.0", 2524 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.1.0.tgz", 2525 | "integrity": "sha512-WCMml9ivU60+8rEJgELlFp1gxFcEGxwYleE3bziHEDeqsqAWGHdimB7beBFGjLzVNgPGyDsfgXLQEYMpmIFnVQ==", 2526 | "dev": true 2527 | }, 2528 | "decamelize": { 2529 | "version": "4.0.0", 2530 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 2531 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 2532 | "dev": true 2533 | } 2534 | } 2535 | } 2536 | } 2537 | } 2538 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-ab", 3 | "version": "1.0.1", 4 | "description": "Middleware for AB testing in Express", 5 | "main": "lib/express-ab.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git@github.com:omichelsen/express-ab.git" 9 | }, 10 | "author": "Ole Michelsen", 11 | "license": "MIT", 12 | "keywords": [ 13 | "ab", 14 | "split", 15 | "test", 16 | "multivariant", 17 | "express" 18 | ], 19 | "scripts": { 20 | "test": "nyc mocha" 21 | }, 22 | "devDependencies": { 23 | "cookie-parser": "^1.4.5", 24 | "express": "^4.17.1", 25 | "mocha": "^8.2.0", 26 | "nyc": "^15.1.0", 27 | "supertest": "^5.0.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/cookies.js: -------------------------------------------------------------------------------- 1 | const ab = require('../lib/express-ab'); 2 | const cookieParser = require('cookie-parser'); 3 | const express = require('express'); 4 | const helpers = require('./helpers'); 5 | const request = require('supertest'); 6 | 7 | describe('cookies', function() { 8 | let app; 9 | let abTest; 10 | 11 | describe('selection', function() { 12 | before(function() { 13 | app = express(); 14 | app.use(cookieParser()); 15 | abTest = ab.test('selection-test'); 16 | app.get('/selection', abTest(), helpers.send('variantA')); 17 | app.get('/selection', abTest(), helpers.send('variantB')); 18 | app.get('/selection', abTest(), helpers.send('variantC')); 19 | }); 20 | 21 | it('should save cookies', function(done) { 22 | request(app) 23 | .get('/selection') 24 | .expect('set-cookie', /^ab=%7B%22selection-test%22%3A0%7D;/, done); 25 | }); 26 | 27 | it('should select route A from cookie', function(done) { 28 | request(app) 29 | .get('/selection') 30 | .set('Cookie', ['ab=%7B%22selection-test%22%3A0%7D']) 31 | .expect(200) 32 | .expect('variantA', done); 33 | }); 34 | 35 | it('should select route C from cookie', function(done) { 36 | request(app) 37 | .get('/selection') 38 | .set('Cookie', ['ab=%7B%22selection-test%22%3A2%7D']) 39 | .expect(200) 40 | .expect('variantC', done); 41 | }); 42 | 43 | it('should not fail on malformed cookie', function(done) { 44 | request(app) 45 | .get('/selection') 46 | .set('Cookie', ['ab=%7B%22selection-test%22%3A%22my-value%22']) 47 | .expect(200) 48 | .end(done); 49 | }); 50 | }); 51 | 52 | describe('rename', function() { 53 | before(function() { 54 | app = express(); 55 | app.use(cookieParser()); 56 | abTest = ab.test('rename-test', { cookie: { name: 'testName' } }); 57 | app.get('/rename', abTest(), helpers.send('variantC')); 58 | }); 59 | 60 | it('should save cookie under new name', function(done) { 61 | request(app) 62 | .get('/rename') 63 | .expect('variantC') 64 | .expect( 65 | 'set-cookie', 66 | 'testName=%7B%22rename-test%22%3A0%7D; Path=/', 67 | done, 68 | ); 69 | }); 70 | }); 71 | 72 | describe('fallthrough to path not in test', function() { 73 | before(function() { 74 | app = express(); 75 | app.use(cookieParser()); 76 | abTest = ab.test('fallthrough-test'); 77 | app.get('/', helpers.setReqVar, abTest('a', 0.25), helpers.sendAb()); 78 | app.get('/', helpers.setReqVar, helpers.sendAb()); 79 | }); 80 | 81 | it('should not return ab test object to fallthrough', function(done) { 82 | request(app) 83 | .get('/') 84 | .set('ab-random', 0.66) 85 | .expect({}) 86 | .expect(function(res) { 87 | if ('set-cookie' in res.headers) return 'set-cookie is present'; 88 | }) 89 | .end(done); 90 | }); 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /test/filter.js: -------------------------------------------------------------------------------- 1 | const ab = require('../lib/express-ab'); 2 | const cookieParser = require('cookie-parser'); 3 | const express = require('express'); 4 | const helpers = require('./helpers'); 5 | const request = require('supertest'); 6 | 7 | describe('filter', function() { 8 | const app = express(); 9 | app.use(cookieParser()); 10 | 11 | const abTest1 = ab.test('filter-test-1'); 12 | const abTest2 = ab.test('filter-test-2'); 13 | 14 | app.get('/', ab.filter(abTest1), abTest2(), helpers.send('2A')); 15 | app.get('/', ab.filter(abTest1), abTest2(), helpers.send('2B')); 16 | app.get('/', helpers.send('fallthrough2')); 17 | 18 | it('should skip test 2 if in test 1', function(done) { 19 | request(app) 20 | .get('/') 21 | .set('Cookie', ['ab=%7B%22filter-test-1%22%3A0%7D']) 22 | .expect('fallthrough2', done); 23 | }); 24 | 25 | it('should pass filter if in test 2', function(done) { 26 | request(app) 27 | .get('/') 28 | .set('Cookie', ['ab=%7B%22filter-test-2%22%3A1%7D']) 29 | .expect('2B', done); 30 | }); 31 | 32 | it('should pass filter if not in test 1', function(done) { 33 | request(app) 34 | .get('/') 35 | .expect('2A', done); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/getVariant.js: -------------------------------------------------------------------------------- 1 | const ab = require('../lib/express-ab'); 2 | const cookieParser = require('cookie-parser'); 3 | const express = require('express'); 4 | const helpers = require('./helpers'); 5 | const request = require('supertest'); 6 | 7 | describe('getVariant', function() { 8 | const app = express(); 9 | app.use(cookieParser()); 10 | 11 | const abTest = ab.test('unit-test'); 12 | 13 | app.get('/', abTest.getVariant, function(req, res) { 14 | if (res.locals.ab) { 15 | res.send('variant' + res.locals.ab.variantId); 16 | } else { 17 | res.send('no cookie'); 18 | } 19 | }); 20 | 21 | it('should read cookie variant from res.locals.ab', function(done) { 22 | request(app) 23 | .get('/') 24 | .set('Cookie', ['ab=%7B%22unit-test%22%3A42%7D']) 25 | .expect('variant42', done); 26 | }); 27 | 28 | it('should continue if no cookie is set', function(done) { 29 | request(app) 30 | .get('/') 31 | .expect('no cookie', done); 32 | }); 33 | 34 | it('should continue if no cookie parser is present', function(done) { 35 | const app = express(); 36 | const abTest = ab.test('unit-test'); 37 | 38 | app.get('/', abTest.getVariant, helpers.send('success')); 39 | 40 | request(app) 41 | .get('/') 42 | .expect('success', done); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/helpers.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | send: function(message) { 3 | return function(req, res) { 4 | res.send(message); 5 | }; 6 | }, 7 | sendAb: function() { 8 | return function(req, res) { 9 | res.send(res.locals.ab); 10 | }; 11 | }, 12 | skipRoute: function(req, res, next) { 13 | next('route'); 14 | }, 15 | setReqVar: function(req, res, next) { 16 | if (!req.ab) { 17 | req.ab = { 18 | random: req.get('ab-random'), 19 | weightSum: 0, 20 | }; 21 | } 22 | next(); 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /test/initialization.js: -------------------------------------------------------------------------------- 1 | const ab = require('../lib/express-ab'); 2 | const assert = require('assert'); 3 | const cookieParser = require('cookie-parser'); 4 | const express = require('express'); 5 | const helpers = require('./helpers'); 6 | const request = require('supertest'); 7 | 8 | describe('initialization', function() { 9 | let app; 10 | let abTest; 11 | 12 | describe('constructor', function() { 13 | before(function() { 14 | ab({ cookie: false }); 15 | 16 | app = express(); 17 | app.use(cookieParser()); 18 | abTest = ab.test('unit-test'); 19 | app.get('/', abTest(), helpers.send('variantA')); 20 | }); 21 | 22 | after(function() { 23 | ab({ cookie: { name: 'ab' } }); 24 | }); 25 | 26 | it('should not send cookies', function(done) { 27 | request(app) 28 | .get('/') 29 | .expect(function(res) { 30 | if ('set-cookie' in res.headers) return 'set-cookie is present'; 31 | }) 32 | .end(done); 33 | }); 34 | }); 35 | 36 | describe('test()', function() { 37 | it('should throw if unnamed', function() { 38 | assert.throws(ab.test); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/passive.js: -------------------------------------------------------------------------------- 1 | const ab = require('../lib/express-ab'); 2 | const cookieParser = require('cookie-parser'); 3 | const express = require('express'); 4 | const request = require('supertest'); 5 | 6 | describe('passive middleware', function() { 7 | const app = express(); 8 | app.use(cookieParser()); 9 | 10 | const abTest = ab.test('unit-test'); 11 | 12 | const variants = ['variantA', 'variantB', 'variantC']; 13 | for (let i = 0; i < variants.length; i++) { 14 | app.use(abTest(variants[i])); 15 | } 16 | 17 | app.get('/', abTest.getVariant, function(req, res) { 18 | res.send(res.locals.ab.variantId); 19 | }); 20 | 21 | it('should assign a cookie and populate res.locals.ab', function(done) { 22 | request(app) 23 | .get('/') 24 | .expect('variantA') 25 | .expect( 26 | 'set-cookie', 27 | /^ab=%7B%22unit-test%22%3A%22variantA%22%7D;/, 28 | done, 29 | ); 30 | }); 31 | 32 | it('should still round robin properly', function(done) { 33 | request(app) 34 | .get('/') 35 | .expect('variantB') 36 | .expect( 37 | 'set-cookie', 38 | /^ab=%7B%22unit-test%22%3A%22variantB%22%7D;/, 39 | done, 40 | ); 41 | }); 42 | 43 | it('should still honor cookies', function(done) { 44 | request(app) 45 | .get('/') 46 | .set('Cookie', ['ab=%7B%22unit-test%22%3A%22variantA%22%7D']) 47 | .expect('variantA', done); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/response.js: -------------------------------------------------------------------------------- 1 | const ab = require('../lib/express-ab'); 2 | const express = require('express'); 3 | const helpers = require('./helpers'); 4 | const request = require('supertest'); 5 | 6 | describe('response', function() { 7 | let app; 8 | let abTest; 9 | 10 | describe('output chosen test', function() { 11 | before(function() { 12 | app = express(); 13 | const abTest1 = ab.test('response-test-1'); 14 | const abTest2 = ab.test('response-test-2'); 15 | app.get('/1', abTest1(), helpers.sendAb()); 16 | app.get('/2', abTest2(), helpers.sendAb()); 17 | }); 18 | 19 | it('should output req.ab object for variant A', function(done) { 20 | request(app) 21 | .get('/1') 22 | .expect( 23 | { 24 | name: 'response-test-1', 25 | variantId: 0, 26 | }, 27 | done, 28 | ); 29 | }); 30 | 31 | it('should output req.ab object for variant B', function(done) { 32 | request(app) 33 | .get('/2') 34 | .expect( 35 | { 36 | name: 'response-test-2', 37 | variantId: 0, 38 | }, 39 | done, 40 | ); 41 | }); 42 | }); 43 | 44 | describe('set id', function() { 45 | before(function() { 46 | app = express(); 47 | abTest = ab.test('id-test', { id: 'google' }); 48 | app.get('/', abTest(), helpers.sendAb()); 49 | }); 50 | 51 | it('should output google id', function(done) { 52 | request(app) 53 | .get('/') 54 | .expect( 55 | { 56 | name: 'id-test', 57 | id: 'google', 58 | variantId: 0, 59 | }, 60 | done, 61 | ); 62 | }); 63 | }); 64 | 65 | describe('fallthrough to path not in test', function() { 66 | before(function() { 67 | app = express(); 68 | abTest = ab.test('fallthrough-test'); 69 | app.get('/', helpers.setReqVar, abTest('a', 0.25), helpers.sendAb()); 70 | app.get('/', helpers.setReqVar, helpers.sendAb()); 71 | }); 72 | 73 | it('should not return ab test object to fallthrough', function(done) { 74 | request(app) 75 | .get('/') 76 | .set('ab-random', 0.66) 77 | .expect({}, done); 78 | }); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /test/round-robin.js: -------------------------------------------------------------------------------- 1 | const ab = require('../lib/express-ab'); 2 | const express = require('express'); 3 | const helpers = require('./helpers'); 4 | const request = require('supertest'); 5 | 6 | describe('round robin', function() { 7 | const app = express(); 8 | const abTest = ab.test('unit-test'); 9 | 10 | app.get('/', abTest(), helpers.send('variantA')); 11 | app.get('/', abTest(), helpers.send('variantB')); 12 | 13 | describe('variant selection', function() { 14 | it('should select route A in first call', function(done) { 15 | request(app) 16 | .get('/') 17 | .expect(200) 18 | .expect('variantA', done); 19 | }); 20 | 21 | it('should select route B in second call', function(done) { 22 | request(app) 23 | .get('/') 24 | .expect(200) 25 | .expect('variantB', done); 26 | }); 27 | 28 | it('should select route A in third call', function(done) { 29 | request(app) 30 | .get('/') 31 | .expect(200) 32 | .expect('variantA', done); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /test/weighted.js: -------------------------------------------------------------------------------- 1 | const ab = require('../lib/express-ab'); 2 | const assert = require('assert'); 3 | const express = require('express'); 4 | const helpers = require('./helpers'); 5 | const request = require('supertest'); 6 | 7 | describe('weighted', function() { 8 | let app; 9 | let abTest; 10 | 11 | describe('variant selection', function() { 12 | before(function() { 13 | app = express(); 14 | abTest = ab.test('variant-test'); 15 | app.get( 16 | '/', 17 | helpers.setReqVar, 18 | abTest(null, 0.2), 19 | helpers.send('variantA'), 20 | ); 21 | app.get( 22 | '/', 23 | helpers.setReqVar, 24 | abTest(null, 0.8), 25 | helpers.send('variantB'), 26 | ); 27 | app.get('/random', abTest(null, 1), function(req, res) { 28 | res.send(req.ab); 29 | }); 30 | }); 31 | 32 | it('should set ab object on req', function(done) { 33 | request(app) 34 | .get('/random') 35 | .expect(function(res) { 36 | assert('random' in res.body); 37 | assert('weightSum' in res.body); 38 | }) 39 | .end(done); 40 | }); 41 | 42 | it('should select route A', function(done) { 43 | request(app) 44 | .get('/') 45 | .set('ab-random', 0.11) 46 | .expect(200) 47 | .expect('variantA', done); 48 | }); 49 | 50 | it('should select route B', function(done) { 51 | request(app) 52 | .get('/') 53 | .set('ab-random', 0.42) 54 | .expect(200) 55 | .expect('variantB', done); 56 | }); 57 | }); 58 | 59 | describe('fallthrough catch-all', function() { 60 | before(function() { 61 | app = express(); 62 | abTest = ab.test('fallthrough-test'); 63 | app.get( 64 | '/', 65 | helpers.setReqVar, 66 | abTest('a', 0.3), 67 | helpers.send('variantA'), 68 | ); 69 | app.get( 70 | '/', 71 | helpers.setReqVar, 72 | abTest('b', 0.3), 73 | helpers.send('variantB'), 74 | ); 75 | app.get('/', helpers.setReqVar, abTest('c'), helpers.send('variantC')); 76 | }); 77 | 78 | it('should fallthrough to non-weighted path', function(done) { 79 | request(app) 80 | .get('/') 81 | .set('ab-random', 0.66) 82 | .expect('variantC', done); 83 | }); 84 | }); 85 | 86 | describe('0%/100% variants', function() { 87 | before(function() { 88 | app = express(); 89 | abTest = ab.test('fallthrough-test'); 90 | app.get('/', helpers.setReqVar, abTest('a', 0), helpers.send('variantA')); 91 | app.get('/', helpers.setReqVar, abTest('b', 1), helpers.send('variantB')); 92 | }); 93 | 94 | it('should always go to variant B when A set to 0', function(done) { 95 | request(app) 96 | .get('/') 97 | .set('ab-random', 0.11) 98 | .expect('variantB', done); 99 | }); 100 | 101 | it('should always go to variant B when A set to 0', function(done) { 102 | request(app) 103 | .get('/') 104 | .set('ab-random', 1) 105 | .expect('variantB', done); 106 | }); 107 | }); 108 | }); 109 | --------------------------------------------------------------------------------