diff --git a/.eslintrc.json b/.eslintrc.json index 0f5dd91c..197d661f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,7 +4,7 @@ "browser": true, "commonjs": true, "es6": true, - "mocha": true, + "jest": true, "node": true }, "extends": ["eslint:recommended", "plugin:prettier/recommended"], @@ -15,7 +15,6 @@ "rules": { "array-bracket-spacing": ["warn", "never"], "arrow-body-style": ["warn", "as-needed"], - "arrow-parens": ["warn", "as-needed"], "arrow-spacing": "warn", "brace-style": ["warn", "1tbs"], "camelcase": "warn", diff --git a/.github/workflows/browser.yml b/.github/workflows/browser.yml new file mode 100644 index 00000000..0d60cb87 --- /dev/null +++ b/.github/workflows/browser.yml @@ -0,0 +1,31 @@ +name: Browser + +on: [push] + +jobs: + ci: + + runs-on: ubuntu-latest + timeout-minutes: 30 + + strategy: + matrix: + node-version: [12.x] + browser: [chrome, edge, firefox, ie, safari] + + steps: + - uses: actions/checkout@v1 + with: + fetch-depth: 1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - name: Test ${{ matrix.node-version }} + run: npm run test:browser + env: + CI: true + BROWSER: ${{ matrix.browser }} + BROWSERSTACK_USER: ${{ secrets.BROWSERSTACK_USER }} + BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} diff --git a/.gitignore b/.gitignore index af0ee801..3785d4a6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ node_modules tmp dist/ +# Browserstack +browserstack.err +local.log diff --git a/README.md b/README.md index 3369e69b..a5f81b20 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,16 @@ uuid.v5('Hello, World!', MY_NAMESPACE); // ⇨ '630eb68f-e0fa-5ecc-887a-7c7a6261 ``` +## Supported Platforms + +- Node.js: All LTS, i.e. 8.x, 10.x, 12.x +- Browsers (with bundlers like webpack/rollup): + - Chrome: >= 49 + - Safari: >= 10 + - Firefox: >= 44 + - Edge: >= 15 + - IE: 11 + ## ECMAScript Modules / ESM For usage in the browser `uuid` provides support for [ECMAScript @@ -96,6 +106,12 @@ uuid(); // ⇨ '1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed' There is experimental native ESM support for [the browser](./examples/browser-esmodules/) but it should not be considered ready for production use and may change or disappear in future releases. +To run the examples you must first build a dist package of this library in the module root: + +``` +npm run package +``` + ## API ### Version 1 (Timestamp + Node) diff --git a/README_js.md b/README_js.md index a63d661d..dc670268 100644 --- a/README_js.md +++ b/README_js.md @@ -89,6 +89,16 @@ const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341'; uuid.v5('Hello, World!', MY_NAMESPACE); // RESULT ``` +## Supported Platforms + +- Node.js: All LTS, i.e. 8.x, 10.x, 12.x +- Browsers (with bundlers like webpack/rollup): + - Chrome: >= 49 + - Safari: >= 10 + - Firefox: >= 44 + - Edge: >= 15 + - IE: 11 + ## ECMAScript Modules / ESM For usage in the browser `uuid` provides support for [ECMAScript @@ -105,6 +115,12 @@ uuid(); // ⇨ '1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed' There is experimental native ESM support for [the browser](./examples/browser-esmodules/) but it should not be considered ready for production use and may change or disappear in future releases. +To run the examples you must first build a dist package of this library in the module root: + +``` +npm run package +``` + ## API ### Version 1 (Timestamp + Node) diff --git a/examples/browser-esmodules/package-lock.json b/examples/browser-esmodules/package-lock.json index e7809454..78163da7 100644 --- a/examples/browser-esmodules/package-lock.json +++ b/examples/browser-esmodules/package-lock.json @@ -5,8 +5,7 @@ "requires": true, "dependencies": { "uuid": { - "version": "github:ctavan/uuid-esm#d93bfcbfc22bfef4c5f021e855f8ceafc08d8f80", - "from": "github:ctavan/uuid-esm#master" + "version": "file:../../dist" } } } diff --git a/examples/browser-esmodules/package.json b/examples/browser-esmodules/package.json index cf82fdb5..57df9815 100644 --- a/examples/browser-esmodules/package.json +++ b/examples/browser-esmodules/package.json @@ -3,9 +3,10 @@ "version": "0.0.0", "private": true, "scripts": { - "test": "npx http-server . -o" + "build": "true", + "start": "npm run build && npx http-server . -o" }, "dependencies": { - "uuid": "github:ctavan/uuid-esm#master" + "uuid": "file:../../dist" } } diff --git a/examples/browser-rollup/README.md b/examples/browser-rollup/README.md index dd51ee91..b1a3a578 100644 --- a/examples/browser-rollup/README.md +++ b/examples/browser-rollup/README.md @@ -2,10 +2,10 @@ ``` npm install -npm test +npm start ``` -Then navigate to `example.html`. +Then navigate to `example-*.html`. The `example-v{1,4}.js` demonstrate that treeshaking works as expected: diff --git a/examples/browser-rollup/example-all.html b/examples/browser-rollup/example-all.html new file mode 100644 index 00000000..b82f17c0 --- /dev/null +++ b/examples/browser-rollup/example-all.html @@ -0,0 +1,9 @@ + + + + UUID esmodule webpack example + + + + + diff --git a/examples/browser-rollup/example-all.js b/examples/browser-rollup/example-all.js index d63b6974..0a091bc2 100644 --- a/examples/browser-rollup/example-all.js +++ b/examples/browser-rollup/example-all.js @@ -1,43 +1,49 @@ import { v1 as uuidv1, v4 as uuidv4, v3 as uuidv3, v5 as uuidv5 } from 'uuid'; import * as uuid from 'uuid'; -console.log('uuidv1()', uuidv1()); - -console.log('uuidv4()', uuidv4()); - -// ... using predefined DNS namespace (for domain names) -console.log('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS)); - -// ... using predefined URL namespace (for, well, URLs) -console.log('uuidv3() URL', uuidv3('http://example.com/hello', uuidv3.URL)); - -// ... using a custom namespace -// -// Note: Custom namespaces should be a UUID string specific to your application! -// E.g. the one here was generated using this modules `uuid` CLI. -const MY_NAMESPACE = '55238d15-c926-4598-b49d-cf4e913ba13c'; -console.log('uuidv3() MY_NAMESPACE', uuidv3('Hello, World!', MY_NAMESPACE)); - -// ... using predefined DNS namespace (for domain names) -console.log('uuidv5() DNS', uuidv5('hello.example.com', uuidv5.DNS)); - -// ... using predefined URL namespace (for, well, URLs) -console.log('uuidv5() URL', uuidv5('http://example.com/hello', uuidv5.URL)); - -// ... using a custom namespace -// -// Note: Custom namespaces should be a UUID string specific to your application! -// E.g. the one here was generated using this modules `uuid` CLI. -// const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341'; -console.log('uuidv5() MY_NAMESPACE', uuidv5('Hello, World!', MY_NAMESPACE)); - -console.log('Same with default export'); - -console.log('uuid.v1()', uuid.v1()); -console.log('uuid.v4()', uuid.v4()); -console.log('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS)); -console.log('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL)); -console.log('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE)); -console.log('uuid.v5() DNS', uuid.v5('hello.example.com', uuid.v5.DNS)); -console.log('uuid.v5() URL', uuid.v5('http://example.com/hello', uuid.v5.URL)); -console.log('uuid.v5() MY_NAMESPACE', uuid.v5('Hello, World!', MY_NAMESPACE)); +import testpage from '../utils/testpage'; + +testpage(function(addTest, done) { + addTest('uuidv1()', uuidv1()); + + addTest('uuidv4()', uuidv4()); + + // ... using predefined DNS namespace (for domain names) + addTest('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS)); + + // ... using predefined URL namespace (for, well, URLs) + addTest('uuidv3() URL', uuidv3('http://example.com/hello', uuidv3.URL)); + + // ... using a custom namespace + // + // Note: Custom namespaces should be a UUID string specific to your application! + // E.g. the one here was generated using this modules `uuid` CLI. + const MY_NAMESPACE = '55238d15-c926-4598-b49d-cf4e913ba13c'; + addTest('uuidv3() MY_NAMESPACE', uuidv3('Hello, World!', MY_NAMESPACE)); + + // ... using predefined DNS namespace (for domain names) + addTest('uuidv5() DNS', uuidv5('hello.example.com', uuidv5.DNS)); + + // ... using predefined URL namespace (for, well, URLs) + addTest('uuidv5() URL', uuidv5('http://example.com/hello', uuidv5.URL)); + + // ... using a custom namespace + // + // Note: Custom namespaces should be a UUID string specific to your application! + // E.g. the one here was generated using this modules `uuid` CLI. + // const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341'; + addTest('uuidv5() MY_NAMESPACE', uuidv5('Hello, World!', MY_NAMESPACE)); + + addTest('Same with default export'); + + addTest('uuid.v1()', uuid.v1()); + addTest('uuid.v4()', uuid.v4()); + addTest('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS)); + addTest('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL)); + addTest('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE)); + addTest('uuid.v5() DNS', uuid.v5('hello.example.com', uuid.v5.DNS)); + addTest('uuid.v5() URL', uuid.v5('http://example.com/hello', uuid.v5.URL)); + addTest('uuid.v5() MY_NAMESPACE', uuid.v5('Hello, World!', MY_NAMESPACE)); + + done(); +}); diff --git a/examples/browser-rollup/example-v1.html b/examples/browser-rollup/example-v1.html new file mode 100644 index 00000000..43c23162 --- /dev/null +++ b/examples/browser-rollup/example-v1.html @@ -0,0 +1,9 @@ + + + + UUID esmodule webpack example + + + + + diff --git a/examples/browser-rollup/example-v1.js b/examples/browser-rollup/example-v1.js index 8e6206f5..2ea885ce 100644 --- a/examples/browser-rollup/example-v1.js +++ b/examples/browser-rollup/example-v1.js @@ -1,3 +1,8 @@ import { v1 as uuidv1 } from 'uuid'; -console.log('uuidv1()', uuidv1()); +import testpage from '../utils/testpage'; + +testpage(function(addTest, done) { + addTest('uuidv1()', uuidv1()); + done(); +}); diff --git a/examples/browser-rollup/example-v4.html b/examples/browser-rollup/example-v4.html new file mode 100644 index 00000000..cc6d057a --- /dev/null +++ b/examples/browser-rollup/example-v4.html @@ -0,0 +1,9 @@ + + + + UUID esmodule webpack example + + + + + diff --git a/examples/browser-rollup/example-v4.js b/examples/browser-rollup/example-v4.js index 100be24b..833724ef 100644 --- a/examples/browser-rollup/example-v4.js +++ b/examples/browser-rollup/example-v4.js @@ -1,3 +1,8 @@ import { v4 as uuidv4 } from 'uuid'; -console.log('uuidv4()', uuidv4()); +import testpage from '../utils/testpage'; + +testpage(function(addTest, done) { + addTest('uuidv4()', uuidv4()); + done(); +}); diff --git a/examples/browser-rollup/package-lock.json b/examples/browser-rollup/package-lock.json index ac174bc3..bbf6f5f8 100644 --- a/examples/browser-rollup/package-lock.json +++ b/examples/browser-rollup/package-lock.json @@ -98,8 +98,7 @@ } }, "uuid": { - "version": "github:ctavan/uuid-esm#8b1920cb00b55f72924888766df03b37b7f05a2d", - "from": "github:ctavan/uuid-esm#master" + "version": "file:../../dist" } } } diff --git a/examples/browser-rollup/package.json b/examples/browser-rollup/package.json index 2da786f2..ab40984a 100644 --- a/examples/browser-rollup/package.json +++ b/examples/browser-rollup/package.json @@ -3,10 +3,11 @@ "version": "0.0.0", "private": true, "scripts": { - "test": "rm -rf dist && rollup -c && npx http-server . -o" + "build": "rm -rf dist && rollup -c", + "start": "npm run build && npx http-server . -o" }, "dependencies": { - "uuid": "github:ctavan/uuid-esm#master" + "uuid": "file:../../dist" }, "devDependencies": { "rollup": "^1.24.0", diff --git a/examples/browser-webpack/README.md b/examples/browser-webpack/README.md index 10bb98e9..ed445a00 100644 --- a/examples/browser-webpack/README.md +++ b/examples/browser-webpack/README.md @@ -2,10 +2,10 @@ ``` npm install -npm test +npm start ``` -Then navigate to `example.html`. +Then navigate to `example-*.html`. The `example-v{1,4}.js` demonstrate that treeshaking works as expected (webpack output below): diff --git a/examples/browser-webpack/example-all.html b/examples/browser-webpack/example-all.html new file mode 100644 index 00000000..b82f17c0 --- /dev/null +++ b/examples/browser-webpack/example-all.html @@ -0,0 +1,9 @@ + + + + UUID esmodule webpack example + + + + + diff --git a/examples/browser-webpack/example-all.js b/examples/browser-webpack/example-all.js index d63b6974..0a091bc2 100644 --- a/examples/browser-webpack/example-all.js +++ b/examples/browser-webpack/example-all.js @@ -1,43 +1,49 @@ import { v1 as uuidv1, v4 as uuidv4, v3 as uuidv3, v5 as uuidv5 } from 'uuid'; import * as uuid from 'uuid'; -console.log('uuidv1()', uuidv1()); - -console.log('uuidv4()', uuidv4()); - -// ... using predefined DNS namespace (for domain names) -console.log('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS)); - -// ... using predefined URL namespace (for, well, URLs) -console.log('uuidv3() URL', uuidv3('http://example.com/hello', uuidv3.URL)); - -// ... using a custom namespace -// -// Note: Custom namespaces should be a UUID string specific to your application! -// E.g. the one here was generated using this modules `uuid` CLI. -const MY_NAMESPACE = '55238d15-c926-4598-b49d-cf4e913ba13c'; -console.log('uuidv3() MY_NAMESPACE', uuidv3('Hello, World!', MY_NAMESPACE)); - -// ... using predefined DNS namespace (for domain names) -console.log('uuidv5() DNS', uuidv5('hello.example.com', uuidv5.DNS)); - -// ... using predefined URL namespace (for, well, URLs) -console.log('uuidv5() URL', uuidv5('http://example.com/hello', uuidv5.URL)); - -// ... using a custom namespace -// -// Note: Custom namespaces should be a UUID string specific to your application! -// E.g. the one here was generated using this modules `uuid` CLI. -// const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341'; -console.log('uuidv5() MY_NAMESPACE', uuidv5('Hello, World!', MY_NAMESPACE)); - -console.log('Same with default export'); - -console.log('uuid.v1()', uuid.v1()); -console.log('uuid.v4()', uuid.v4()); -console.log('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS)); -console.log('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL)); -console.log('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE)); -console.log('uuid.v5() DNS', uuid.v5('hello.example.com', uuid.v5.DNS)); -console.log('uuid.v5() URL', uuid.v5('http://example.com/hello', uuid.v5.URL)); -console.log('uuid.v5() MY_NAMESPACE', uuid.v5('Hello, World!', MY_NAMESPACE)); +import testpage from '../utils/testpage'; + +testpage(function(addTest, done) { + addTest('uuidv1()', uuidv1()); + + addTest('uuidv4()', uuidv4()); + + // ... using predefined DNS namespace (for domain names) + addTest('uuidv3() DNS', uuidv3('hello.example.com', uuidv3.DNS)); + + // ... using predefined URL namespace (for, well, URLs) + addTest('uuidv3() URL', uuidv3('http://example.com/hello', uuidv3.URL)); + + // ... using a custom namespace + // + // Note: Custom namespaces should be a UUID string specific to your application! + // E.g. the one here was generated using this modules `uuid` CLI. + const MY_NAMESPACE = '55238d15-c926-4598-b49d-cf4e913ba13c'; + addTest('uuidv3() MY_NAMESPACE', uuidv3('Hello, World!', MY_NAMESPACE)); + + // ... using predefined DNS namespace (for domain names) + addTest('uuidv5() DNS', uuidv5('hello.example.com', uuidv5.DNS)); + + // ... using predefined URL namespace (for, well, URLs) + addTest('uuidv5() URL', uuidv5('http://example.com/hello', uuidv5.URL)); + + // ... using a custom namespace + // + // Note: Custom namespaces should be a UUID string specific to your application! + // E.g. the one here was generated using this modules `uuid` CLI. + // const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341'; + addTest('uuidv5() MY_NAMESPACE', uuidv5('Hello, World!', MY_NAMESPACE)); + + addTest('Same with default export'); + + addTest('uuid.v1()', uuid.v1()); + addTest('uuid.v4()', uuid.v4()); + addTest('uuid.v3() DNS', uuid.v3('hello.example.com', uuid.v3.DNS)); + addTest('uuid.v3() URL', uuid.v3('http://example.com/hello', uuid.v3.URL)); + addTest('uuid.v3() MY_NAMESPACE', uuid.v3('Hello, World!', MY_NAMESPACE)); + addTest('uuid.v5() DNS', uuid.v5('hello.example.com', uuid.v5.DNS)); + addTest('uuid.v5() URL', uuid.v5('http://example.com/hello', uuid.v5.URL)); + addTest('uuid.v5() MY_NAMESPACE', uuid.v5('Hello, World!', MY_NAMESPACE)); + + done(); +}); diff --git a/examples/browser-webpack/example-v1.html b/examples/browser-webpack/example-v1.html new file mode 100644 index 00000000..43c23162 --- /dev/null +++ b/examples/browser-webpack/example-v1.html @@ -0,0 +1,9 @@ + + + + UUID esmodule webpack example + + + + + diff --git a/examples/browser-webpack/example-v1.js b/examples/browser-webpack/example-v1.js index 8e6206f5..95e648df 100644 --- a/examples/browser-webpack/example-v1.js +++ b/examples/browser-webpack/example-v1.js @@ -1,3 +1,9 @@ import { v1 as uuidv1 } from 'uuid'; -console.log('uuidv1()', uuidv1()); +import testpage from '../utils/testpage'; + +testpage(function(addTest, done) { + addTest('uuidv1()', uuidv1()); + + done(); +}); diff --git a/examples/browser-webpack/example-v4.html b/examples/browser-webpack/example-v4.html new file mode 100644 index 00000000..cc6d057a --- /dev/null +++ b/examples/browser-webpack/example-v4.html @@ -0,0 +1,9 @@ + + + + UUID esmodule webpack example + + + + + diff --git a/examples/browser-webpack/example-v4.js b/examples/browser-webpack/example-v4.js index 100be24b..b43ed203 100644 --- a/examples/browser-webpack/example-v4.js +++ b/examples/browser-webpack/example-v4.js @@ -1,3 +1,9 @@ import { v4 as uuidv4 } from 'uuid'; -console.log('uuidv4()', uuidv4()); +import testpage from '../utils/testpage'; + +testpage(function(addTest, done) { + addTest('uuidv4()', uuidv4()); + + done(); +}); diff --git a/examples/browser-webpack/package-lock.json b/examples/browser-webpack/package-lock.json index 7ac8cdbe..00ec4ae3 100644 --- a/examples/browser-webpack/package-lock.json +++ b/examples/browser-webpack/package-lock.json @@ -3868,8 +3868,7 @@ "dev": true }, "uuid": { - "version": "github:ctavan/uuid-esm#d93bfcbfc22bfef4c5f021e855f8ceafc08d8f80", - "from": "github:ctavan/uuid-esm#master" + "version": "file:../../dist" }, "v8-compile-cache": { "version": "2.0.3", diff --git a/examples/browser-webpack/package.json b/examples/browser-webpack/package.json index 58fb29cf..82f360e0 100644 --- a/examples/browser-webpack/package.json +++ b/examples/browser-webpack/package.json @@ -3,10 +3,11 @@ "version": "0.0.0", "private": true, "scripts": { - "test": "rm -rf dist && webpack && npx http-server . -o" + "build": "rm -rf dist && webpack", + "start": "npm run build && npx http-server . -o" }, "dependencies": { - "uuid": "github:ctavan/uuid-esm#master" + "uuid": "file:../../dist" }, "devDependencies": { "webpack": "^4.40.2", diff --git a/examples/browser-webpack/webpack.config.js b/examples/browser-webpack/webpack.config.js index 8ce81b4d..d1711b25 100644 --- a/examples/browser-webpack/webpack.config.js +++ b/examples/browser-webpack/webpack.config.js @@ -1,4 +1,7 @@ module.exports = { + resolve: { + extensions: ['*', '.js'], + }, entry: { all: './example-all.js', v1: './example-v1.js', diff --git a/examples/node-commonjs/package-lock.json b/examples/node-commonjs/package-lock.json index c48e72fa..1c363b84 100644 --- a/examples/node-commonjs/package-lock.json +++ b/examples/node-commonjs/package-lock.json @@ -5,8 +5,7 @@ "requires": true, "dependencies": { "uuid": { - "version": "github:ctavan/uuid-esm#4d60b66275298da97903e39b0b1e0114f40b1a07", - "from": "github:ctavan/uuid-esm#master" + "version": "file:../../dist" } } } diff --git a/examples/node-commonjs/package.json b/examples/node-commonjs/package.json index e4c69602..cd70d89e 100644 --- a/examples/node-commonjs/package.json +++ b/examples/node-commonjs/package.json @@ -6,6 +6,6 @@ "test": "node example.js" }, "dependencies": { - "uuid": "github:ctavan/uuid-esm#master" + "uuid": "file:../../dist" } } diff --git a/examples/utils/testpage.js b/examples/utils/testpage.js new file mode 100644 index 00000000..245c52f3 --- /dev/null +++ b/examples/utils/testpage.js @@ -0,0 +1,28 @@ +export default function test(callback) { + const ul = document.createElement('ul'); + document.body.appendChild(ul); + + function addTest(title, result) { + const li = document.createElement('li'); + + const h2 = document.createElement('h2'); + h2.innerHTML = title; + li.appendChild(h2); + + const p = document.createElement('p'); + p.innerHTML = result; + li.appendChild(p); + + ul.appendChild(li); + } + + function done() { + const div = document.createElement('div'); + div.id = 'done'; + document.body.appendChild(div); + } + + window.onload = function() { + callback(addTest, done); + }; +} diff --git a/package-lock.json b/package-lock.json index 253e354a..23415aa8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1728,6 +1728,15 @@ "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", "dev": true }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, "aggregate-error": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", @@ -1896,6 +1905,15 @@ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, "async-each": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", @@ -2165,6 +2183,12 @@ } } }, + "basic-auth": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz", + "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ=", + "dev": true + }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -2254,6 +2278,18 @@ "node-releases": "^1.1.29" } }, + "browserstack-local": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.4.2.tgz", + "integrity": "sha512-fRaynjF0MvtyyfPRy2NFnVwxLyNtD28K/v9xRsIjUVf7xLc80NIm7Nfr3KXlFmWizhG91PL/UAOXlHkoxQjaNw==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1", + "is-running": "^2.0.0", + "ps-tree": "=1.1.1", + "temp-fs": "^0.9.9" + } + }, "bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -2596,6 +2632,12 @@ "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", "dev": true }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -3145,6 +3187,12 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", + "dev": true + }, "cosmiconfig": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", @@ -3523,6 +3571,12 @@ } } }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -3533,6 +3587,18 @@ "safer-buffer": "^2.1.0" } }, + "ecstatic": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz", + "integrity": "sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog==", + "dev": true, + "requires": { + "he": "^1.1.1", + "mime": "^1.6.0", + "minimist": "^1.1.0", + "url-join": "^2.0.5" + } + }, "electron-to-chromium": { "version": "1.3.282", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.282.tgz", @@ -3598,6 +3664,21 @@ "is-symbol": "^1.0.2" } }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -3797,6 +3878,38 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "dev": true, + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + }, + "dependencies": { + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "dev": true, + "requires": { + "through": "2" + } + } + } + }, + "eventemitter3": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", + "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", + "dev": true + }, "exec-sh": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", @@ -4175,6 +4288,26 @@ "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", "dev": true }, + "follow-redirects": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.9.0.tgz", + "integrity": "sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A==", + "dev": true, + "requires": { + "debug": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -4207,6 +4340,12 @@ "map-cache": "^0.2.2" } }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", + "dev": true + }, "fs-access": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", @@ -5285,6 +5424,12 @@ } } }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, "hosted-git-info": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", @@ -5300,6 +5445,35 @@ "whatwg-encoding": "^1.0.1" } }, + "http-proxy": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", + "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-server": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.12.0.tgz", + "integrity": "sha512-imGLDSTT1BZ0QG1rBFnaZ6weK5jeisUnCxZQI1cpYTdz0luPUM5e3s+WU5zRWEkiI6DQxL2p54oeKrDlzO6bRw==", + "dev": true, + "requires": { + "basic-auth": "^1.0.3", + "colors": "^1.3.3", + "corser": "^2.0.1", + "ecstatic": "^3.3.2", + "http-proxy": "^1.17.0", + "opener": "^1.5.1", + "optimist": "~0.6.1", + "portfinder": "^1.0.20", + "secure-compare": "3.0.1", + "union": "~0.5.0" + } + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -5311,6 +5485,27 @@ "sshpk": "^1.7.0" } }, + "https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "dev": true, + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, "husky": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/husky/-/husky-3.0.9.tgz", @@ -5394,6 +5589,12 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, "import-fresh": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz", @@ -5823,6 +6024,12 @@ "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", "dev": true }, + "is-running": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-running/-/is-running-2.1.0.tgz", + "integrity": "sha1-MKc/9cw4VOT8JUkICen1q/jeCeA=", + "dev": true + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -6612,6 +6819,18 @@ "verror": "1.10.0" } }, + "jszip": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.2.2.tgz", + "integrity": "sha512-NmKajvAFQpbg3taXQXr/ccS2wcucR1AZ+NtyWp2Nq7HHVsXhcJFR8p0Baf32C2yVvBylFWVeKf+WI2AnvlPhpA==", + "dev": true, + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + } + }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -6646,6 +6865,15 @@ "type-check": "~0.3.2" } }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", @@ -7149,6 +7377,12 @@ "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", "dev": true }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "dev": true + }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", @@ -7158,6 +7392,12 @@ "object-visit": "^1.0.0" } }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true + }, "meow": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", @@ -7208,6 +7448,12 @@ "to-regex": "^3.0.2" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, "mime-db": { "version": "1.40.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", @@ -7414,6 +7660,36 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -7566,6 +7842,12 @@ "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==", "dev": true }, + "opener": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", + "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", + "dev": true + }, "optimist": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", @@ -7675,6 +7957,12 @@ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, + "pako": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", + "dev": true + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7760,6 +8048,15 @@ "pify": "^3.0.0" } }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "dev": true, + "requires": { + "through": "~2.3" + } + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -7772,6 +8069,12 @@ "integrity": "sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA==", "dev": true }, + "pidtree": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz", + "integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==", + "dev": true + }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -7877,6 +8180,28 @@ "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", "dev": true }, + "portfinder": { + "version": "1.0.25", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", + "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.1" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -7952,6 +8277,15 @@ "sisteransi": "^1.0.3" } }, + "ps-tree": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.1.1.tgz", + "integrity": "sha512-kef7fYYSKVqQffmzTMsVcUD1ObNJMp8sNSmHGlGKsZQyL/ht9MZKk86u0Rd1NhpTOAuhqwKCLLpktwkqz+MF8A==", + "dev": true, + "requires": { + "event-stream": "=3.3.4" + } + }, "psl": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", @@ -8259,6 +8593,12 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, "resolve": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", @@ -8427,6 +8767,35 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, + "secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=", + "dev": true + }, + "selenium-webdriver": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", + "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", + "dev": true, + "requires": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + }, + "dependencies": { + "tmp": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.1" + } + } + } + }, "semver": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", @@ -8445,6 +8814,12 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, "set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", @@ -8483,6 +8858,12 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "dev": true + }, "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", @@ -9010,6 +9391,15 @@ "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", "dev": true }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "dev": true, + "requires": { + "duplexer": "~0.1.1" + } + }, "string-argv": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", @@ -9036,6 +9426,17 @@ "strip-ansi": "^4.0.0" } }, + "string.prototype.padend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz", + "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.4.3", + "function-bind": "^1.0.2" + } + }, "string.prototype.trimleft": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", @@ -9182,6 +9583,26 @@ } } }, + "temp-fs": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/temp-fs/-/temp-fs-0.9.9.tgz", + "integrity": "sha1-gHFzBDeHByDpQxUy/igUNk+IA9c=", + "dev": true, + "requires": { + "rimraf": "~2.5.2" + }, + "dependencies": { + "rimraf": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", + "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + } + } + }, "test-exclude": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", @@ -9479,6 +9900,15 @@ "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==", "dev": true }, + "union": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", + "dev": true, + "requires": { + "qs": "^6.4.0" + } + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -9553,6 +9983,12 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, + "url-join": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", + "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", + "dev": true + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -9759,6 +10195,23 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "xml2js": { + "version": "0.4.22", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.22.tgz", + "integrity": "sha512-MWTbxAQqclRSTnehWWe5nMKzI3VmJ8ltiJEco8akcC6j3miOhjjfzKum5sId+CWhfxdOs/1xauYr8/ZDBtQiRw==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "util.promisify": "~1.0.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", diff --git a/package.json b/package.json index 22e0cc68..7740897a 100644 --- a/package.json +++ b/package.json @@ -27,22 +27,31 @@ "@commitlint/config-conventional": "~8.2.0", "babel-eslint": "~10.0.3", "babel-plugin-add-module-exports": "~1.0.2", + "browserstack-local": "~1.4.2", "eslint": "~6.4.0", "eslint-config-prettier": "~6.4.0", "eslint-plugin-prettier": "~3.1.1", "esm": "~3.2.25", + "http-server": "~0.12.0", "husky": "~3.0.9", "jest": "~24.9.0", "lint-staged": "~9.4.3", + "npm-run-all": "~4.1.5", "prettier": "~1.18.2", "runmd": "1.2.1", + "selenium-webdriver": "~3.6.0", "standard-version": "7.0.0" }, "scripts": { + "examples:browser-webpack:build": "cd examples/browser-webpack && npm install && npm run build", + "examples:browser-rollup:build": "cd examples/browser-rollup && npm install && npm run build", + "examples:browser-esmodules:build": "cd examples/browser-esmodules && npm install && npm run build", "lint": "npm run eslint:check && npm run prettier:check", "eslint:check": "eslint src/ test/ examples/ *.js", "eslint:fix": "eslint --fix src/ test/ examples/ *.js", - "test": "BABEL_ENV=commonjs jest test/", + "test": "BABEL_ENV=commonjs jest test/unit/", + "pretest:browser": "npm run package && npm-run-all --parallel examples:**", + "test:browser": "BABEL_ENV=commonjs jest --forceExit --verbose test/browser/${BROWSER:-}*", "prettier:check": "prettier --ignore-path .prettierignore --check '**/*.{js,jsx,json,md}'", "prettier:fix": "prettier --ignore-path .prettierignore --write '**/*.{js,jsx,json,md}'", "ci": "npm run lint && npm run test && npm run prettier:check && npm run docs:diff", diff --git a/test/browser/browser-test.js b/test/browser/browser-test.js new file mode 100644 index 00000000..0263795c --- /dev/null +++ b/test/browser/browser-test.js @@ -0,0 +1,167 @@ +/* eslint-disable no-restricted-globals, camelcase */ +const { Builder, By, until } = require('selenium-webdriver'); +const browserstack = require('browserstack-local'); +const httpServer = require('http-server'); + +const v1Regex = new RegExp( + /^[0-9A-F]{8}-[0-9A-F]{4}-1[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i, +); +const v4Regex = new RegExp( + /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i, +); + +const v1 = (result) => expect(result).toMatch(v1Regex); +const v4 = (result) => expect(result).toMatch(v4Regex); +const v3dns = (result) => expect(result).toBe('9125a8dc-52ee-365b-a5aa-81b0b3681cf6'); +const v3url = (result) => expect(result).toBe('c6235813-3ba4-3801-ae84-e0a6ebb7d138'); +const v3custom = (result) => expect(result).toBe('f5a52d34-dcd7-30f7-b581-0112fab43d0c'); +const v5dns = (result) => expect(result).toBe('fdda765f-fc57-5604-a269-52a7df8164ec'); +const v5url = (result) => expect(result).toBe('3bbcee75-cecc-5b56-8031-b6641c1ed1f1'); +const v5custom = (result) => expect(result).toBe('c49c5142-4d9a-5940-a926-612ede0ec632'); +const ignore = (result) => true; + +const expectations = { + 'uuidv1()': v1, + 'uuidv4()': v4, + 'uuidv3() DNS': v3dns, + 'uuidv3() URL': v3url, + 'uuidv3() MY_NAMESPACE': v3custom, + 'uuidv5() DNS': v5dns, + 'uuidv5() URL': v5url, + 'uuidv5() MY_NAMESPACE': v5custom, + 'Same with default export': ignore, + 'uuid.v1()': v1, + 'uuid.v4()': v4, + 'uuid.v3() DNS': v3dns, + 'uuid.v3() URL': v3url, + 'uuid.v3() MY_NAMESPACE': v3custom, + 'uuid.v5() DNS': v5dns, + 'uuid.v5() URL': v5url, + 'uuid.v5() MY_NAMESPACE': v5custom, +}; +const expectationTitles = Object.keys(expectations); + +const PROJECT = process.env.GITHUB_REPOSITORY || 'node-uuid'; +const GITHUB_SHA = process.env.GITHUB_SHA || ''; +const GITHUB_REF = process.env.GITHUB_REF || ''; +const BUILD = GITHUB_SHA || GITHUB_REF ? `${GITHUB_REF} ${GITHUB_SHA}` : 'manual build'; + +module.exports = function(localIdentifier, PORT, capabilityMatrix) { + describe(`browser ${localIdentifier}`, () => { + jest.setTimeout(180000); + + let server; + beforeAll((done) => { + server = httpServer.createServer({ + root: `${__dirname}/../../examples`, + }); + server.listen(PORT, '0.0.0.0', done); + }); + + let bsLocal; + beforeAll((done) => { + bsLocal = new browserstack.Local(); + bsLocal.start( + { + key: process.env.BROWSERSTACK_ACCESS_KEY, + onlyAutomate: 'true', + verbose: 'true', + localIdentifier, + }, + done, + ); + }); + + afterAll((done) => { + bsLocal.stop(done); + }); + + afterAll(() => { + server.close(); + }); + + capabilityMatrix.forEach((additionalCapabilities) => { + describe(Object.values(additionalCapabilities).join(' '), () => { + let browser; + beforeAll(async () => { + var capabilities = { + resolution: '1024x768', + 'browserstack.user': process.env.BROWSERSTACK_USER, + 'browserstack.key': process.env.BROWSERSTACK_ACCESS_KEY, + 'browserstack.local': true, + 'browserstack.localIdentifier': localIdentifier, + project: PROJECT, + build: BUILD, + name: 'browser test', + ...additionalCapabilities, + }; + browser = await new Builder() + .usingServer('http://hub-cloud.browserstack.com/wd/hub') + .withCapabilities(capabilities) + .build(); + }); + afterAll(async () => { + try { + await browser.close(); + } catch (error) {} + try { + await browser.quit(); + } catch (error) {} + }); + + async function testExpectations(path, titleFilter) { + const url = `http://127.0.0.1:${PORT}/${path}`; + await browser.get(url); + await browser.wait(until.elementLocated(By.id('done')), 30000); + const elements = await browser.findElements(By.css('li')); + + // Unfortunately the WebDriver API is not thread safe and we cannot use Promise.all() to + // query it in parallel: + // https://github.com/SeleniumHQ/selenium/issues/422#issuecomment-90629726 + const titles = []; + for (let i = 0; i < elements.length; i++) { + const element = elements[i]; + const h2 = await element.findElement(By.css('h2')); + const title = await h2.getText(); + const p = await element.findElement(By.css('p')); + const result = await p.getText(); + expectations[title](result); + titles.push(title); + } + + expect(titles).toEqual(expectationTitles.filter(titleFilter)); + } + + describe('webpack', () => { + test('it renders all', async () => + testExpectations('browser-webpack/example-all.html', () => true)); + + test('it renders v1 only', async () => + testExpectations('browser-webpack/example-v1.html', (title) => + title.includes('uuidv1'), + )); + + test('it renders v4 only', async () => + testExpectations('browser-webpack/example-v4.html', (title) => + title.includes('uuidv4'), + )); + }); + + describe('rollup', () => { + test('it renders all', async () => + testExpectations('browser-rollup/example-all.html', () => true)); + + test('it renders v1 only', async () => + testExpectations('browser-rollup/example-v1.html', (title) => + title.includes('uuidv1'), + )); + + test('it renders v4 only', async () => + testExpectations('browser-rollup/example-v4.html', (title) => + title.includes('uuidv4'), + )); + }); + }); + }); + }); +}; diff --git a/test/browser/chrome.test.js b/test/browser/chrome.test.js new file mode 100644 index 00000000..94115c3b --- /dev/null +++ b/test/browser/chrome.test.js @@ -0,0 +1,17 @@ +/* eslint-disable no-restricted-globals, camelcase */ +const browserTest = require('./browser-test'); + +browserTest('chrome', 9000, [ + { + browserName: 'Chrome', + browser_version: '79.0', + os: 'Windows', + os_version: '10', + }, + { + browserName: 'Chrome', + browser_version: '49.0', + os: 'Windows', + os_version: '10', + }, +]); diff --git a/test/browser/edge.test.js b/test/browser/edge.test.js new file mode 100644 index 00000000..7c9a5bdf --- /dev/null +++ b/test/browser/edge.test.js @@ -0,0 +1,23 @@ +/* eslint-disable no-restricted-globals, camelcase */ +const browserTest = require('./browser-test'); + +browserTest('edge', 9001, [ + { + browserName: 'Edge', + browser_version: '79.0 beta', + os: 'Windows', + os_version: '10', + }, + { + browserName: 'Edge', + browser_version: '18.0', + os: 'Windows', + os_version: '10', + }, + { + browserName: 'Edge', + browser_version: '15.0', + os: 'Windows', + os_version: '10', + }, +]); diff --git a/test/browser/firefox.test.js b/test/browser/firefox.test.js new file mode 100644 index 00000000..d417fc7c --- /dev/null +++ b/test/browser/firefox.test.js @@ -0,0 +1,17 @@ +/* eslint-disable no-restricted-globals, camelcase */ +const browserTest = require('./browser-test'); + +browserTest('firefox', 9002, [ + { + browserName: 'Firefox', + browser_version: '70.0', + os: 'Windows', + os_version: '10', + }, + { + browserName: 'Firefox', + browser_version: '44.0', + os: 'Windows', + os_version: '10', + }, +]); diff --git a/test/browser/ie.test.js b/test/browser/ie.test.js new file mode 100644 index 00000000..aba68721 --- /dev/null +++ b/test/browser/ie.test.js @@ -0,0 +1,17 @@ +/* eslint-disable no-restricted-globals, camelcase */ +const browserTest = require('./browser-test'); + +browserTest('ie', 9003, [ + { + browserName: 'IE', + browser_version: '11.0', + os: 'Windows', + os_version: '10', + }, + { + browserName: 'IE', + browser_version: '11.0', + os: 'Windows', + os_version: '7', + }, +]); diff --git a/test/browser/safari.test.js b/test/browser/safari.test.js new file mode 100644 index 00000000..90ec0ca0 --- /dev/null +++ b/test/browser/safari.test.js @@ -0,0 +1,17 @@ +/* eslint-disable no-restricted-globals, camelcase */ +const browserTest = require('./browser-test'); + +browserTest('safari', 9004, [ + { + browserName: 'Safari', + browser_version: '13.0', + os: 'OS X', + os_version: 'Catalina', + }, + { + browserName: 'Safari', + browser_version: '10.0', + os: 'OS X', + os_version: 'Sierra', + }, +]); diff --git a/test/test.js b/test/unit/unit.test.js similarity index 95% rename from test/test.js rename to test/unit/unit.test.js index d6518ac8..c5b0d648 100644 --- a/test/test.js +++ b/test/unit/unit.test.js @@ -1,13 +1,13 @@ import assert from 'assert'; -import md5 from '../src/md5.js'; -import md5Browser from '../src/md5-browser.js'; -import rng from '../src/rng.js'; -import rngBrowser from '../src/rng-browser.js'; -import sha1 from '../src/sha1.js'; -import sha1Browser from '../src/sha1-browser.js'; -import v1 from '../src/v1.js'; -import v3 from '../src/v3.js'; -import v5 from '../src/v5.js'; +import md5 from '../../src/md5.js'; +import md5Browser from '../../src/md5-browser.js'; +import rng from '../../src/rng.js'; +import rngBrowser from '../../src/rng-browser.js'; +import sha1 from '../../src/sha1.js'; +import sha1Browser from '../../src/sha1-browser.js'; +import v1 from '../../src/v1.js'; +import v3 from '../../src/v3.js'; +import v5 from '../../src/v5.js'; describe('rng', () => { test('nodeRNG', () => {