diff --git a/.eslintrc b/.eslintrc index 709a876a0..1b0df8be3 100644 --- a/.eslintrc +++ b/.eslintrc @@ -52,7 +52,8 @@ 1, "single" ], - "react/prop-types": 0 + "react/prop-types": 0, + "semi": [1, "always"] }, "env": { "browser": true, diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..3b0f14a32 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,17 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false \ No newline at end of file diff --git a/.meteor/release b/.meteor/release index 4c71956e2..e76dedee1 100644 --- a/.meteor/release +++ b/.meteor/release @@ -1 +1 @@ -METEOR@1.7.0.5 +METEOR@1.8 diff --git a/.meteor/versions b/.meteor/versions index 416b36950..38d6261ef 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -1,79 +1,82 @@ -accounts-base@1.4.2 +accounts-base@1.4.3 accounts-password@1.5.1 allow-deny@1.1.0 apollo@3.0.0 -autoupdate@1.4.0 -babel-compiler@7.1.1 -babel-runtime@1.2.4 +autoupdate@1.5.0 +babel-compiler@7.2.1 +babel-runtime@1.3.0 base64@1.0.11 -binary-heap@1.0.10 +binary-heap@1.0.11 blaze-tools@1.0.10 -boilerplate-generator@1.5.0 +boilerplate-generator@1.6.0 buffer@0.0.0 -caching-compiler@1.1.12 -caching-html-compiler@1.1.2 +caching-compiler@1.2.0 +caching-html-compiler@1.1.3 callback-hook@1.1.0 check@1.3.1 ddp@1.4.0 -ddp-client@2.3.2 +ddp-client@2.3.3 ddp-common@1.4.0 ddp-rate-limiter@1.0.7 ddp-server@2.2.0 deps@1.0.12 diff-sequence@1.1.0 -dynamic-import@0.4.2 -ecmascript@0.11.1 +dynamic-import@0.5.0 +ecmascript@0.12.1 ecmascript-runtime@0.7.0 -ecmascript-runtime-client@0.7.1 -ecmascript-runtime-server@0.7.0 +ecmascript-runtime-client@0.8.0 +ecmascript-runtime-server@0.7.1 ejson@1.1.0 email@1.2.3 es5-shim@4.8.0 -fourseven:scss@4.5.4 +fetch@0.1.0 +fourseven:scss@4.10.0 geojson-utils@1.0.10 hot-code-push@1.0.4 html-tools@1.0.11 htmljs@1.0.11 http@1.4.1 id-map@1.1.0 -lmieulet:meteor-coverage@1.1.4 +inter-process-messaging@0.1.0 +lmieulet:meteor-coverage@2.0.2 localstorage@1.2.0 logging@1.1.20 -meteor@1.9.0 +meteor@1.9.2 meteorhacks:inject-initial@1.0.4 meteorhacks:picker@1.0.3 -meteortesting:browser-tests@1.0.0 -meteortesting:mocha@1.0.0 -minifier-css@1.3.1 -minifier-js@2.3.5 -minimongo@1.4.4 -modern-browsers@0.1.1 -modules@0.12.2 -modules-runtime@0.10.0 -mongo@1.5.0 +meteortesting:browser-tests@1.1.0 +meteortesting:mocha@1.0.1 +meteortesting:mocha-core@1.0.1 +minifier-css@1.4.0 +minifier-js@2.4.0 +minimongo@1.4.5 +modern-browsers@0.1.2 +modules@0.13.0 +modules-runtime@0.10.3 +mongo@1.6.0 +mongo-decimal@0.1.0 mongo-dev-server@1.1.0 mongo-id@1.0.7 npm-bcrypt@0.9.3 -npm-mongo@3.0.7 +npm-mongo@3.1.1 ordered-dict@1.1.0 percolatestudio:synced-cron@1.1.0 -practicalmeteor:mocha-core@1.0.1 promise@0.11.1 random@1.1.0 rate-limit@1.0.9 reactive-var@1.0.11 reload@1.2.0 retry@1.1.0 -routepolicy@1.0.13 +routepolicy@1.1.0 server-render@0.3.1 service-configuration@1.0.11 sha@1.0.9 -shell-server@0.3.1 +shell-server@0.4.0 socket-stream-client@0.2.2 spacebars-compiler@1.1.3 -srp@1.0.10 -standard-minifier-css@1.4.1 -standard-minifier-js@2.3.4 +srp@1.0.12 +standard-minifier-css@1.5.1 +standard-minifier-js@2.4.0 static-html@1.2.2 templating-tools@1.1.2 tracker@1.2.0 @@ -85,6 +88,7 @@ vulcan:email@1.12.8 vulcan:i18n@1.12.8 vulcan:i18n-en-us@1.12.8 vulcan:lib@1.12.8 +vulcan:routing@1.12.8 vulcan:users@1.12.8 -webapp@1.6.1 +webapp@1.7.0 webapp-hashing@1.0.9 diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 000000000..80ae81bc0 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,21 @@ +'use strict'; + +const {esNextPaths} = require('./.vulcan/shared/pathsByLanguageVersion'); + +module.exports = { + bracketSpacing: false, + singleQuote: true, + jsxBracketSameLine: true, + trailingComma: 'es5', + printWidth: 80, + parser: 'babylon', + + overrides: [ + { + files: esNextPaths, + options: { + trailingComma: 'all', + }, + }, + ], +}; \ No newline at end of file diff --git a/.vulcan/prettier/index.js b/.vulcan/prettier/index.js new file mode 100644 index 000000000..3008683dc --- /dev/null +++ b/.vulcan/prettier/index.js @@ -0,0 +1,76 @@ + +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict'; + +// Based on similar script in Jest +// https://github.com/facebook/jest/blob/a7acc5ae519613647ff2c253dd21933d6f94b47f/scripts/prettier.js + +const chalk = require('chalk'); +const glob = require('glob'); +const prettier = require('prettier'); +const fs = require('fs'); +const listChangedFiles = require('../shared/listChangedFiles'); +const prettierConfigPath = require.resolve('../../.prettierrc'); + +const mode = process.argv[2] || 'check'; +const shouldWrite = mode === 'write' || mode === 'write-changed'; +const onlyChanged = mode === 'check-changed' || mode === 'write-changed'; + +const changedFiles = onlyChanged ? listChangedFiles() : null; +let didWarn = false; +let didError = false; + +const files = glob + .sync('**/*.js', {ignore: '**/node_modules/**'}) + .filter(f => !onlyChanged || changedFiles.has(f)); + +if (!files.length) { + return; +} + +files.forEach(file => { + const options = prettier.resolveConfig.sync(file, { + config: prettierConfigPath, + }); + try { + const input = fs.readFileSync(file, 'utf8'); + if (shouldWrite) { + const output = prettier.format(input, options); + if (output !== input) { + fs.writeFileSync(file, output, 'utf8'); + } + } else { + if (!prettier.check(input, options)) { + if (!didWarn) { + console.log( + '\n' + + chalk.red( + ` This project uses prettier to format all JavaScript code.\n` + ) + + chalk.dim(` Please run `) + + chalk.reset('yarn prettier-all') + + chalk.dim( + ` and add changes to files listed below to your commit:` + ) + + `\n\n` + ); + didWarn = true; + } + console.log(file); + } + } + } catch (error) { + didError = true; + console.log('\n\n' + error.message); + console.log(file); + } +}); + +if (didWarn || didError) { + process.exit(1); +} \ No newline at end of file diff --git a/.vulcan/shared/listChangedFiles.js b/.vulcan/shared/listChangedFiles.js new file mode 100644 index 000000000..65dad80af --- /dev/null +++ b/.vulcan/shared/listChangedFiles.js @@ -0,0 +1,36 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict'; + +const execFileSync = require('child_process').execFileSync; + +const exec = (command, args) => { + console.log('> ' + [command].concat(args).join(' ')); + const options = { + cwd: process.cwd(), + env: process.env, + stdio: 'pipe', + encoding: 'utf-8', + }; + return execFileSync(command, args, options); +}; + +const execGitCmd = args => + exec('git', args) + .trim() + .toString() + .split('\n'); + +const listChangedFiles = () => { + const mergeBase = execGitCmd(['merge-base', 'HEAD', 'devel']); + return new Set([ + ...execGitCmd(['diff', '--name-only', '--diff-filter=ACMRTUB', mergeBase]), + ...execGitCmd(['ls-files', '--others', '--exclude-standard']), + ]); +}; + +module.exports = listChangedFiles; diff --git a/.vulcan/shared/pathsByLanguageVersion.js b/.vulcan/shared/pathsByLanguageVersion.js new file mode 100644 index 000000000..1e875de0c --- /dev/null +++ b/.vulcan/shared/pathsByLanguageVersion.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict'; + +// Files that are transformed and can use ES6/Flow/JSX. +const esNextPaths = [ + // Internal forwarding modules + 'packages/*/*.js', + 'packages/*/*.jsx', +]; + +module.exports = { + esNextPaths, +}; \ No newline at end of file diff --git a/README.md b/README.md index ff69b3139..61bbca622 100644 --- a/README.md +++ b/README.md @@ -50,18 +50,16 @@ You can find the even older, non-React version of Telescope on the [legacy](http This project exists thanks to all the people who contribute. - + ### Backers -Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/vulcan#backer)] +Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/vulcan#contribute)] - + ### Sponsors -Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/vulcan#sponsor)] - - - +Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/vulcan#contribute)] + diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 251c84d7f..000000000 --- a/package-lock.json +++ /dev/null @@ -1,9718 +0,0 @@ -{ - "name": "Vulcan", - "version": "1.12.8", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@apollographql/apollo-upload-server": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@apollographql/apollo-upload-server/-/apollo-upload-server-5.0.3.tgz", - "integrity": "sha512-tGAp3ULNyoA8b5o9LsU2Lq6SwgVPUOKAqKywu2liEtTvrFSGPrObwanhYwArq3GPeOqp2bi+JknSJCIU3oQN1Q==", - "requires": { - "@babel/runtime-corejs2": "^7.0.0-rc.1", - "busboy": "^0.2.14", - "object-path": "^0.11.4" - } - }, - "@apollographql/graphql-playground-html": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.0.tgz", - "integrity": "sha512-QAZIFrfVRkjvMkUHIQKZXZ3La0V5t12w5PWrhihYEabHwzIZV/txQd/kSYHgYPXC4s5OURxsXZop9f0BzI2QIQ==" - }, - "@babel/code-frame": { - "version": "7.0.0-beta.54", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.54.tgz", - "integrity": "sha1-ACT5b99wKKIdaOJzr9TpUyFKHq0=", - "dev": true, - "requires": { - "@babel/highlight": "7.0.0-beta.54" - } - }, - "@babel/highlight": { - "version": "7.0.0-beta.54", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.54.tgz", - "integrity": "sha1-FV1Qc1gym45waJcAF8P9dKmwhYQ=", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^3.0.0" - } - }, - "@babel/runtime": { - "version": "7.0.0-beta.55", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0-beta.55.tgz", - "integrity": "sha1-C8M6paasCwEvN+JbnmqqLkiakWs=", - "requires": { - "core-js": "^2.5.7", - "regenerator-runtime": "^0.12.0" - }, - "dependencies": { - "core-js": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" - }, - "regenerator-runtime": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", - "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" - } - } - }, - "@babel/runtime-corejs2": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.1.2.tgz", - "integrity": "sha512-drxaPByExlcRDKW4ZLubUO4ZkI8/8ax9k9wve1aEthdLKFzjB7XRkOQ0xoTIWGxqdDnWDElkjYq77bt7yrcYJQ==", - "requires": { - "core-js": "^2.5.7", - "regenerator-runtime": "^0.12.0" - }, - "dependencies": { - "core-js": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" - }, - "regenerator-runtime": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", - "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" - } - } - }, - "@f/animate": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@f/animate/-/animate-1.0.1.tgz", - "integrity": "sha1-oDE5itrfmgvTpWOYzskx+HfYhIU=", - "requires": { - "@f/elapsed-time": "^1.0.0", - "@f/raf": "^1.0.0", - "@f/tween": "^1.0.0" - } - }, - "@f/elapsed-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@f/elapsed-time/-/elapsed-time-1.0.0.tgz", - "integrity": "sha1-ageaYQSocni/W0CARE7wLRtZVEk=", - "requires": { - "@f/timestamp": "^1.0.0" - } - }, - "@f/map-obj": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@f/map-obj/-/map-obj-1.2.2.tgz", - "integrity": "sha1-2an4vXbKoq4RtjPdok2cbMzB5g0=" - }, - "@f/raf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@f/raf/-/raf-1.0.3.tgz", - "integrity": "sha1-Mt3KN940WyDIw4QwGMxRuPiXkU0=" - }, - "@f/timestamp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@f/timestamp/-/timestamp-1.0.0.tgz", - "integrity": "sha1-MqkWbiUW5cy5sPz9yJIjgZcQ6Iw=" - }, - "@f/tween": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@f/tween/-/tween-1.0.1.tgz", - "integrity": "sha1-GK73nEl15UQVrfMm5LXg0FPSB/A=", - "requires": { - "@f/map-obj": "^1.2.2" - } - }, - "@material-ui/core": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-3.2.0.tgz", - "integrity": "sha512-fThWolLWQoUMV8exLxe9NqfegFFHmV8T7kJBvk3iX47c7TrAw4Vgyvft4CApnQoYIZbrRfPMig5ovYDvmTmMCg==", - "requires": { - "@babel/runtime": "7.1.2", - "@types/jss": "^9.5.6", - "@types/react-transition-group": "^2.0.8", - "brcast": "^3.0.1", - "classnames": "^2.2.5", - "csstype": "^2.5.2", - "debounce": "^1.1.0", - "deepmerge": "^2.0.1", - "dom-helpers": "^3.2.1", - "hoist-non-react-statics": "^2.5.0", - "is-plain-object": "^2.0.4", - "jss": "^9.3.3", - "jss-camel-case": "^6.0.0", - "jss-default-unit": "^8.0.2", - "jss-global": "^3.0.0", - "jss-nested": "^6.0.1", - "jss-props-sort": "^6.0.0", - "jss-vendor-prefixer": "^7.0.0", - "keycode": "^2.1.9", - "normalize-scroll-left": "^0.1.2", - "popper.js": "^1.14.1", - "prop-types": "^15.6.0", - "react-event-listener": "^0.6.2", - "react-transition-group": "^2.2.1", - "recompose": "0.28.0 - 0.30.0", - "warning": "^4.0.1" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.1.2.tgz", - "integrity": "sha512-Y3SCjmhSupzFB6wcv1KmmFucH6gDVnI30WjOcicV10ju0cZjak3Jcs67YLIXBrmZYw1xCrVeJPbycFwrqNyxpg==", - "requires": { - "regenerator-runtime": "^0.12.0" - } - }, - "deepmerge": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", - "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==" - }, - "hoist-non-react-statics": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", - "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" - }, - "recompose": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.30.0.tgz", - "integrity": "sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==", - "requires": { - "@babel/runtime": "^7.0.0", - "change-emitter": "^0.1.2", - "fbjs": "^0.8.1", - "hoist-non-react-statics": "^2.3.1", - "react-lifecycles-compat": "^3.0.2", - "symbol-observable": "^1.0.4" - } - }, - "regenerator-runtime": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", - "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" - }, - "warning": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.2.tgz", - "integrity": "sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==", - "requires": { - "loose-envify": "^1.0.0" - } - } - } - }, - "@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" - }, - "@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" - }, - "@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", - "requires": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" - }, - "@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" - }, - "@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" - }, - "@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" - }, - "@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" - }, - "@segment/loosely-validate-event": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-1.1.2.tgz", - "integrity": "sha1-13hAmZ4/fkPnSzsNQzkcFSb3k7g=", - "requires": { - "component-type": "^1.2.1", - "join-component": "^1.1.0" - } - }, - "@types/accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/async": { - "version": "2.0.50", - "resolved": "https://registry.npmjs.org/@types/async/-/async-2.0.50.tgz", - "integrity": "sha512-VMhZMMQgV1zsR+lX/0IBfAk+8Eb7dPVMWiQGFAt3qjo5x7Ml6b77jUo0e1C3ToD+XRDXqtrfw+6AB0uUsPEr3Q==", - "optional": true - }, - "@types/body-parser": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", - "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.32", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", - "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", - "requires": { - "@types/node": "*" - } - }, - "@types/cors": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.4.tgz", - "integrity": "sha512-ipZjBVsm2tF/n8qFGOuGBkUij9X9ZswVi9G3bx/6dz7POpVa6gVHcj1wsX/LVEn9MMF41fxK/PnZPPoTD1UFPw==", - "requires": { - "@types/express": "*" - } - }, - "@types/events": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", - "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" - }, - "@types/express": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.0.tgz", - "integrity": "sha512-TtPEYumsmSTtTetAPXlJVf3kEqb6wZK0bZojpJQrnD/djV4q1oB6QQ8aKvKqwNPACoe02GNiy5zDzcYivR5Z2w==", - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.0.tgz", - "integrity": "sha512-lTeoCu5NxJU4OD9moCgm0ESZzweAx0YqsAcab6OB0EB3+As1OaHtKnaGJvcngQxYsi9UNv0abn4/DRavrRxt4w==", - "requires": { - "@types/events": "*", - "@types/node": "*", - "@types/range-parser": "*" - } - }, - "@types/jss": { - "version": "9.5.6", - "resolved": "https://registry.npmjs.org/@types/jss/-/jss-9.5.6.tgz", - "integrity": "sha512-7TWmR5y1jYG4ka4wTZt65RR0kw4WgALFUWktQIWbLnDd6/z/0SQZ/4+UeH0rhdp+HEdIfmzPBH0VwE/4Z9Evzw==", - "requires": { - "csstype": "^2.0.0", - "indefinite-observable": "^1.0.1" - } - }, - "@types/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz", - "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==" - }, - "@types/mime": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.0.tgz", - "integrity": "sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA==" - }, - "@types/node": { - "version": "9.6.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.21.tgz", - "integrity": "sha512-zQS6mHzxEstR8Vvnpc3JDUCDGWnHFzMTcBu9UCZoVLuj1Uvkkk0qFKJQEhlvbsX34m3xt12ejV09eO/ljZcn7A==" - }, - "@types/prop-types": { - "version": "15.5.6", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.6.tgz", - "integrity": "sha512-ZBFR7TROLVzCkswA3Fmqq+IIJt62/T7aY/Dmz+QkU7CaW2QFqAitCE8Ups7IzmGhcN1YWMBT4Qcoc07jU9hOJQ==" - }, - "@types/range-parser": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.2.tgz", - "integrity": "sha512-HtKGu+qG1NPvYe1z7ezLsyIaXYyi8SoAVqWDZgDQ8dLrsZvSzUNCwZyfX33uhWxL/SU0ZDQZ3nwZ0nimt507Kw==" - }, - "@types/react": { - "version": "16.4.16", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.4.16.tgz", - "integrity": "sha512-lxyoipLWweAnLnSsV4Ho2NAZTKKmxeYgkTQ6PaDiPDU9JJBUY2zJVVGiK1smzYv8+ZgbqEmcm5xM74GCpunSEA==", - "requires": { - "@types/prop-types": "*", - "csstype": "^2.2.0" - } - }, - "@types/react-transition-group": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-2.0.14.tgz", - "integrity": "sha512-pa7qB0/mkhwWMBFoXhX8BcntK8G4eQl4sIfSrJCxnivTYRQWjOWf2ClR9bWdm0EUFBDHzMbKYS+QYfDtBzkY4w==", - "requires": { - "@types/react": "*" - } - }, - "@types/serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", - "requires": { - "@types/express-serve-static-core": "*", - "@types/mime": "*" - } - }, - "@types/ws": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-5.1.2.tgz", - "integrity": "sha512-NkTXUKTYdXdnPE2aUUbGOXE1XfMK527SCvU/9bj86kyFF6kZ9ZnOQ3mK5jADn98Y2vEUD/7wKDgZa7Qst2wYOg==", - "requires": { - "@types/events": "*", - "@types/node": "*" - } - }, - "@types/zen-observable": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.0.tgz", - "integrity": "sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==" - }, - "abab": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", - "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=" - }, - "accepts": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", - "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", - "requires": { - "mime-types": "~2.1.16", - "negotiator": "0.6.1" - } - }, - "acorn": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", - "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==", - "dev": true - }, - "acorn-globals": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz", - "integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==", - "dev": true, - "requires": { - "acorn": "^5.0.0" - } - }, - "acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", - "dev": true, - "requires": { - "acorn": "^3.0.4" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - } - } - }, - "ajv": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.4.0.tgz", - "integrity": "sha1-MtHPCNvIDEMvQm8S4QslEfa0ZHQ=", - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "ajv-keywords": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", - "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", - "dev": true - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - } - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" - }, - "analytics-node": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/analytics-node/-/analytics-node-2.4.1.tgz", - "integrity": "sha1-H5bI64h7bEdpEESsf8mhIx+wIPc=", - "requires": { - "@segment/loosely-validate-event": "^1.1.2", - "clone": "^2.1.1", - "commander": "^2.9.0", - "crypto-token": "^1.0.1", - "debug": "^2.6.2", - "lodash": "^4.17.4", - "remove-trailing-slash": "^0.1.0", - "superagent": "^3.5.0", - "superagent-retry": "^0.6.0" - } - }, - "ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" - }, - "apollo-cache": { - "version": "1.1.17", - "resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.1.17.tgz", - "integrity": "sha512-7sp24n2HZO4vXgTaKNomLyIfGxG4gDdDkBB0jkRzRi7HhnKmfwhiF/RCiKNbgLdrPX151INdls0KwIVliD0dHQ==", - "requires": { - "apollo-utilities": "^1.0.21" - }, - "dependencies": { - "apollo-utilities": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.0.21.tgz", - "integrity": "sha512-ZcxELlEl+sDCYBgEMdNXJAsZtRVm8wk4HIA58bMsqYfd1DSAJQEtZ93F0GZgYNAGy3QyaoBeZtbb0/01++G8JQ==", - "requires": { - "fast-json-stable-stringify": "^2.0.0", - "fclone": "^1.0.11" - } - } - } - }, - "apollo-cache-control": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/apollo-cache-control/-/apollo-cache-control-0.2.5.tgz", - "integrity": "sha512-xEDrUvo3U2mQKSzA8NzQmgeqK4ytwFnTGl2YKGKPfG0+r8fZdswKp6CDBue29KLO8KkSuqW/hntveWrAdK51FQ==", - "requires": { - "apollo-server-env": "^2.0.3", - "graphql-extensions": "^0.2.1" - } - }, - "apollo-cache-inmemory": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/apollo-cache-inmemory/-/apollo-cache-inmemory-1.2.10.tgz", - "integrity": "sha512-eBusPFVtYIuo+PIfJdAwUCQ4cs7AJ4mB7sTdXxNQCXToYw8mzE6EfHnV37kdVfBXSaa82BzE2rb/YUq/duuamw==", - "requires": { - "apollo-cache": "^1.1.17", - "apollo-utilities": "^1.0.21", - "graphql-anywhere": "^4.1.19" - }, - "dependencies": { - "apollo-utilities": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.0.21.tgz", - "integrity": "sha512-ZcxELlEl+sDCYBgEMdNXJAsZtRVm8wk4HIA58bMsqYfd1DSAJQEtZ93F0GZgYNAGy3QyaoBeZtbb0/01++G8JQ==", - "requires": { - "fast-json-stable-stringify": "^2.0.0", - "fclone": "^1.0.11" - } - }, - "graphql-anywhere": { - "version": "4.1.19", - "resolved": "https://registry.npmjs.org/graphql-anywhere/-/graphql-anywhere-4.1.19.tgz", - "integrity": "sha512-mQlvbECzYPBcgBC9JsdM4v+DSvNmcIP+8Vwr+ij3gktbaLSE0Iza30mztuz40Jlf7ooMs+0emBZucNjLzqz7tA==", - "requires": { - "apollo-utilities": "^1.0.21" - } - } - } - }, - "apollo-client": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/apollo-client/-/apollo-client-2.4.5.tgz", - "integrity": "sha512-nUm06EGa4TP/IY68OzmC3lTD32TqkjLOQdb69uYo+lHl8NnwebtrAw3qFtsQtTEz6ueBp/Z/HasNZng4jwafVQ==", - "requires": { - "@types/async": "2.0.50", - "@types/zen-observable": "^0.8.0", - "apollo-cache": "1.1.20", - "apollo-link": "^1.0.0", - "apollo-link-dedup": "^1.0.0", - "apollo-utilities": "1.0.25", - "symbol-observable": "^1.0.2", - "zen-observable": "^0.8.0" - }, - "dependencies": { - "apollo-cache": { - "version": "1.1.20", - "resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.1.20.tgz", - "integrity": "sha512-+Du0/4kUSuf5PjPx0+pvgMGV12ezbHA8/hubYuqRQoy/4AWb4faa61CgJNI6cKz2mhDd9m94VTNKTX11NntwkQ==", - "requires": { - "apollo-utilities": "^1.0.25" - } - }, - "apollo-utilities": { - "version": "1.0.25", - "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.0.25.tgz", - "integrity": "sha512-AXvqkhni3Ir1ffm4SA1QzXn8k8I5BBl4PVKEyak734i4jFdp+xgfUyi2VCqF64TJlFTA/B73TRDUvO2D+tKtZg==", - "requires": { - "fast-json-stable-stringify": "^2.0.0" - } - } - } - }, - "apollo-datasource": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/apollo-datasource/-/apollo-datasource-0.1.3.tgz", - "integrity": "sha512-yEGEe5Cjzqqu5ml1VV3O8+C+thzdknZri9Ny0P3daTGNO+45J3vBOMcmaANeeI2+OOeWxdqUNa5aPOx/35kniw==", - "requires": { - "apollo-server-caching": "0.1.2", - "apollo-server-env": "2.0.3" - } - }, - "apollo-engine": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/apollo-engine/-/apollo-engine-0.5.4.tgz", - "integrity": "sha512-91yqiM1fB33fjvcsIBICy8BUHh2cG9FAIrteCh9QaL7UwJ6aQMV5DSfjNhgP95DEZcPMggKQGLhbW156A7G0mg==", - "requires": { - "apollo-engine-binary-darwin": "^0.2017.11-40-g9585bfc6", - "apollo-engine-binary-linux": "^0.2017.11-40-g9585bfc6", - "apollo-engine-binary-windows": "^0.2017.11-40-g9585bfc6", - "request": "^2.81.0", - "stream-line-wrapper": "^0.1.1" - } - }, - "apollo-engine-binary-darwin": { - "version": "0.2017.11-84-gb299b9188", - "resolved": "https://registry.npmjs.org/apollo-engine-binary-darwin/-/apollo-engine-binary-darwin-0.2017.11-84-gb299b9188.tgz", - "integrity": "sha512-8zKIFo6ldSwT1npHU4gjHMDEJQuN/CG3MCnx5xY5MGSSkqlqNZZ8njYgXe4qLEjewLMwRTXapcnCw7E2+H1RYQ==", - "optional": true - }, - "apollo-engine-binary-linux": { - "version": "0.2017.11-84-gb299b9188", - "resolved": "https://registry.npmjs.org/apollo-engine-binary-linux/-/apollo-engine-binary-linux-0.2017.11-84-gb299b9188.tgz", - "integrity": "sha512-Y+DYYoR24yi73+Kt03Nr7IXNoMJw6faEgdUpysMdnkIdmqaFfcKj3KH0auzVBhPyVcJo+iRTKqXdnMzjnQxrsg==", - "optional": true - }, - "apollo-engine-binary-windows": { - "version": "0.2017.11-84-gb299b9188", - "resolved": "https://registry.npmjs.org/apollo-engine-binary-windows/-/apollo-engine-binary-windows-0.2017.11-84-gb299b9188.tgz", - "integrity": "sha512-ecpP1HrlP+eb5mNQuz7ObzMWtGJA78UrPlzGRes1KiKJ/c8e1UrrAWI/wuI0Ry7fIKYA6dUzxJ4fHR5TEnMAVA==", - "optional": true - }, - "apollo-engine-reporting": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/apollo-engine-reporting/-/apollo-engine-reporting-0.0.6.tgz", - "integrity": "sha512-JmfNJ9v3QEJQ8ZhLfCKEDiww53n5kj5HarP85p8LreoYNojbvcWN8Qm6RgvSG5N/ZJrAYHeTRQbSxm1vWwGubw==", - "requires": { - "apollo-engine-reporting-protobuf": "^0.0.1", - "apollo-server-env": "^2.0.3", - "async-retry": "^1.2.1", - "graphql-extensions": "^0.2.1", - "lodash": "^4.17.10" - }, - "dependencies": { - "graphql-extensions": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/graphql-extensions/-/graphql-extensions-0.2.1.tgz", - "integrity": "sha512-/1FTPSWSffDjlRyMAV2UwQhojLmca9aQD0ieo1IYiqT5SE+uOWi4r83QF1CoER0sREIsH3s+nTmdH3cvQVG3MA==", - "requires": { - "apollo-server-env": "^2.0.3" - } - } - } - }, - "apollo-engine-reporting-protobuf": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/apollo-engine-reporting-protobuf/-/apollo-engine-reporting-protobuf-0.0.1.tgz", - "integrity": "sha512-AySoDgog2p1Nph44FyyqaU4AfRZOXx8XZxRsVHvYY4dHlrMmDDhhjfF3Jswa7Wr8X/ivvx3xA0jimRn6rsG8Ew==", - "requires": { - "protobufjs": "^6.8.6" - } - }, - "apollo-errors": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/apollo-errors/-/apollo-errors-1.5.1.tgz", - "integrity": "sha512-gYAceMzNJfF+mUHH2/4UcZTkZtDY54arCTKGbKa7WU5IXnTJ4V+P94wHodcDkLLHWpHL8SW1hEgjN5ZINcPb1w==", - "requires": { - "es6-error": "^4.0.0" - } - }, - "apollo-link": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.3.tgz", - "integrity": "sha512-iL9yS2OfxYhigme5bpTbmRyC+Htt6tyo2fRMHT3K1XRL/C5IQDDz37OjpPy4ndx7WInSvfSZaaOTKFja9VWqSw==", - "requires": { - "apollo-utilities": "^1.0.0", - "zen-observable-ts": "^0.8.10" - } - }, - "apollo-link-dedup": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/apollo-link-dedup/-/apollo-link-dedup-1.0.10.tgz", - "integrity": "sha512-tpUI9lMZsidxdNygSY1FxflXEkUZnvKRkMUsXXuQUNoSLeNtEvUX7QtKRAl4k9ubLl8JKKc9X3L3onAFeGTK8w==", - "requires": { - "apollo-link": "^1.2.3" - } - }, - "apollo-link-schema": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/apollo-link-schema/-/apollo-link-schema-1.1.1.tgz", - "integrity": "sha512-zlyt9jlHMK+vwW7jPPetR5oHI/gIdbLmExqVCjJm8xx7KEdP9xKBCy13Zd6r0EYE9tNt5ygWohne6WYtdXBtWQ==", - "requires": { - "apollo-link": "^1.2.3" - } - }, - "apollo-link-state": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/apollo-link-state/-/apollo-link-state-0.4.2.tgz", - "integrity": "sha512-xMPcAfuiPVYXaLwC6oJFIZrKgV3GmdO31Ag2eufRoXpvT0AfJZjdaPB4450Nu9TslHRePN9A3quxNueILlQxlw==", - "requires": { - "apollo-utilities": "^1.0.8", - "graphql-anywhere": "^4.1.0-alpha.0" - }, - "dependencies": { - "graphql-anywhere": { - "version": "4.1.19", - "resolved": "https://registry.npmjs.org/graphql-anywhere/-/graphql-anywhere-4.1.19.tgz", - "integrity": "sha512-mQlvbECzYPBcgBC9JsdM4v+DSvNmcIP+8Vwr+ij3gktbaLSE0Iza30mztuz40Jlf7ooMs+0emBZucNjLzqz7tA==", - "requires": { - "apollo-utilities": "^1.0.21" - }, - "dependencies": { - "apollo-utilities": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.0.21.tgz", - "integrity": "sha512-ZcxELlEl+sDCYBgEMdNXJAsZtRVm8wk4HIA58bMsqYfd1DSAJQEtZ93F0GZgYNAGy3QyaoBeZtbb0/01++G8JQ==", - "requires": { - "fast-json-stable-stringify": "^2.0.0", - "fclone": "^1.0.11" - } - } - } - } - } - }, - "apollo-server": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/apollo-server/-/apollo-server-2.1.0.tgz", - "integrity": "sha512-Uo5RFHGtUPq3OvycLXCll5QgXf2wNVBFYUhapByADBP4E1KRgbyl9Fbf82OgcbbLYwEZTlQMbyBpd6hX8XJKAw==", - "requires": { - "apollo-server-core": "^2.1.0", - "apollo-server-express": "^2.1.0", - "express": "^4.0.0", - "graphql-subscriptions": "^0.5.8", - "graphql-tools": "^3.0.4" - }, - "dependencies": { - "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", - "requires": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" - } - }, - "apollo-cache-control": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/apollo-cache-control/-/apollo-cache-control-0.2.5.tgz", - "integrity": "sha512-xEDrUvo3U2mQKSzA8NzQmgeqK4ytwFnTGl2YKGKPfG0+r8fZdswKp6CDBue29KLO8KkSuqW/hntveWrAdK51FQ==", - "requires": { - "apollo-server-env": "^2.0.3", - "graphql-extensions": "^0.2.1" - } - }, - "apollo-link": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.3.tgz", - "integrity": "sha512-iL9yS2OfxYhigme5bpTbmRyC+Htt6tyo2fRMHT3K1XRL/C5IQDDz37OjpPy4ndx7WInSvfSZaaOTKFja9VWqSw==", - "requires": { - "apollo-utilities": "^1.0.0", - "zen-observable-ts": "^0.8.10" - } - }, - "apollo-server-core": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-2.1.0.tgz", - "integrity": "sha512-D1Tw0o3NzCQ2KGM8EWh9AHELHmn/SE361dtlqJxkbelxXqAkCIGIFywF30h+0ezhMbgbO7eqBBJfvRilF/oJHA==", - "requires": { - "@apollographql/apollo-upload-server": "^5.0.3", - "@types/ws": "^5.1.2", - "apollo-cache-control": "^0.2.5", - "apollo-datasource": "^0.1.3", - "apollo-engine-reporting": "^0.0.6", - "apollo-server-caching": "^0.1.2", - "apollo-server-env": "^2.0.3", - "apollo-server-errors": "^2.0.2", - "apollo-tracing": "^0.2.5", - "graphql-extensions": "^0.2.1", - "graphql-subscriptions": "^0.5.8", - "graphql-tag": "^2.9.2", - "graphql-tools": "^3.0.4", - "hash.js": "^1.1.3", - "lodash": "^4.17.10", - "subscriptions-transport-ws": "^0.9.11", - "ws": "^5.2.0" - } - }, - "apollo-server-express": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-2.1.0.tgz", - "integrity": "sha512-jLFIz1VLduMA/rme4OAy3IPeoaMEZOPoQXpio8AhfjIqCijRPPfoWJ2QMqz56C/g3vas7rZtgcVOrHpjBKudjw==", - "requires": { - "@apollographql/apollo-upload-server": "^5.0.3", - "@apollographql/graphql-playground-html": "^1.6.0", - "@types/accepts": "^1.3.5", - "@types/body-parser": "1.17.0", - "@types/cors": "^2.8.4", - "@types/express": "4.16.0", - "accepts": "^1.3.5", - "apollo-server-core": "^2.1.0", - "body-parser": "^1.18.3", - "cors": "^2.8.4", - "graphql-subscriptions": "^0.5.8", - "graphql-tools": "^3.0.4", - "type-is": "^1.6.16" - } - }, - "apollo-tracing": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/apollo-tracing/-/apollo-tracing-0.2.5.tgz", - "integrity": "sha512-DZO7pfL5LATHeJdVFoTZ/N3HwA+IMf1YnIt5K+uMQW+/MrRgYOtTszUv5tYX2cUIqHYHcbdDaBQUuIXwSpaV2Q==", - "requires": { - "apollo-server-env": "^2.0.3", - "graphql-extensions": "^0.2.1" - } - }, - "body-parser": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", - "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", - "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", - "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", - "qs": "6.5.2", - "raw-body": "2.3.3", - "type-is": "~1.6.16" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "graphql-extensions": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/graphql-extensions/-/graphql-extensions-0.2.1.tgz", - "integrity": "sha512-/1FTPSWSffDjlRyMAV2UwQhojLmca9aQD0ieo1IYiqT5SE+uOWi4r83QF1CoER0sREIsH3s+nTmdH3cvQVG3MA==", - "requires": { - "apollo-server-env": "^2.0.3" - } - }, - "graphql-tag": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.0.tgz", - "integrity": "sha512-9FD6cw976TLLf9WYIUPCaaTpniawIjHWZSwIRZSjrfufJamcXbVVYfN2TWvJYbw0Xf2JjYbl1/f2+wDnBVw3/w==" - }, - "graphql-tools": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/graphql-tools/-/graphql-tools-3.1.1.tgz", - "integrity": "sha512-yHvPkweUB0+Q/GWH5wIG60bpt8CTwBklCSzQdEHmRUgAdEQKxw+9B7zB3dG7wB3Ym7M7lfrS4Ej+jtDZfA2UXg==", - "requires": { - "apollo-link": "^1.2.2", - "apollo-utilities": "^1.0.1", - "deprecated-decorator": "^0.1.6", - "iterall": "^1.1.3", - "uuid": "^3.1.0" - } - }, - "http-errors": { - "version": "1.6.3", - "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "mime-db": { - "version": "1.36.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", - "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==" - }, - "mime-types": { - "version": "2.1.20", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz", - "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==", - "requires": { - "mime-db": "~1.36.0" - } - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "raw-body": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", - "unpipe": "1.0.0" - } - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" - }, - "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.18" - } - }, - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "requires": { - "async-limiter": "~1.0.0" - } - }, - "zen-observable-ts": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.10.tgz", - "integrity": "sha512-5vqMtRggU/2GhePC9OU4sYEWOdvmayp2k3gjPf4F0mXwB3CSbbNznfDUvDJx9O2ZTa1EIXdJhPchQveFKwNXPQ==", - "requires": { - "zen-observable": "^0.8.0" - } - } - } - }, - "apollo-server-caching": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/apollo-server-caching/-/apollo-server-caching-0.1.2.tgz", - "integrity": "sha512-jBRnsTgXN0m8yVpumoelaUq9mXR7YpJ3EE+y/alI7zgXY+0qFDqksRApU8dEfg3q6qUnO7rFxRhdG5eyc0+1ig==", - "requires": { - "lru-cache": "^4.1.3" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", - "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - } - } - }, - "apollo-server-core": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-2.1.0.tgz", - "integrity": "sha512-D1Tw0o3NzCQ2KGM8EWh9AHELHmn/SE361dtlqJxkbelxXqAkCIGIFywF30h+0ezhMbgbO7eqBBJfvRilF/oJHA==", - "requires": { - "@apollographql/apollo-upload-server": "^5.0.3", - "@types/ws": "^5.1.2", - "apollo-cache-control": "^0.2.5", - "apollo-datasource": "^0.1.3", - "apollo-engine-reporting": "^0.0.6", - "apollo-server-caching": "^0.1.2", - "apollo-server-env": "^2.0.3", - "apollo-server-errors": "^2.0.2", - "apollo-tracing": "^0.2.5", - "graphql-extensions": "^0.2.1", - "graphql-subscriptions": "^0.5.8", - "graphql-tag": "^2.9.2", - "graphql-tools": "^3.0.4", - "hash.js": "^1.1.3", - "lodash": "^4.17.10", - "subscriptions-transport-ws": "^0.9.11", - "ws": "^5.2.0" - }, - "dependencies": { - "apollo-link": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.3.tgz", - "integrity": "sha512-iL9yS2OfxYhigme5bpTbmRyC+Htt6tyo2fRMHT3K1XRL/C5IQDDz37OjpPy4ndx7WInSvfSZaaOTKFja9VWqSw==", - "requires": { - "apollo-utilities": "^1.0.0", - "zen-observable-ts": "^0.8.10" - } - }, - "graphql-tag": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.0.tgz", - "integrity": "sha512-9FD6cw976TLLf9WYIUPCaaTpniawIjHWZSwIRZSjrfufJamcXbVVYfN2TWvJYbw0Xf2JjYbl1/f2+wDnBVw3/w==" - }, - "graphql-tools": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/graphql-tools/-/graphql-tools-3.1.1.tgz", - "integrity": "sha512-yHvPkweUB0+Q/GWH5wIG60bpt8CTwBklCSzQdEHmRUgAdEQKxw+9B7zB3dG7wB3Ym7M7lfrS4Ej+jtDZfA2UXg==", - "requires": { - "apollo-link": "^1.2.2", - "apollo-utilities": "^1.0.1", - "deprecated-decorator": "^0.1.6", - "iterall": "^1.1.3", - "uuid": "^3.1.0" - } - }, - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "requires": { - "async-limiter": "~1.0.0" - } - } - } - }, - "apollo-server-env": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/apollo-server-env/-/apollo-server-env-2.0.3.tgz", - "integrity": "sha512-uIfKFH8n8xKO0eLb9Fa79+s2DdMuVethgznvW6SrOYq5VzgkIIobqKEuZPKa5wObw9CkCyju/+Sr7b7WWMFxUQ==", - "requires": { - "node-fetch": "^2.1.2", - "util.promisify": "^1.0.0" - }, - "dependencies": { - "node-fetch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.2.0.tgz", - "integrity": "sha512-OayFWziIxiHY8bCUyLX6sTpDH8Jsbp4FfYd1j1f7vZyfgkcOnAyM4oQR16f8a0s7Gl/viMGRey8eScYk4V4EZA==" - } - } - }, - "apollo-server-errors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/apollo-server-errors/-/apollo-server-errors-2.0.2.tgz", - "integrity": "sha512-zyWDqAVDCkj9espVsoUpZr9PwDznM8UW6fBfhV+i1br//s2AQb07N6ektZ9pRIEvkhykDZW+8tQbDwAO0vUROg==" - }, - "apollo-server-express": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-2.1.0.tgz", - "integrity": "sha512-jLFIz1VLduMA/rme4OAy3IPeoaMEZOPoQXpio8AhfjIqCijRPPfoWJ2QMqz56C/g3vas7rZtgcVOrHpjBKudjw==", - "requires": { - "@apollographql/apollo-upload-server": "^5.0.3", - "@apollographql/graphql-playground-html": "^1.6.0", - "@types/accepts": "^1.3.5", - "@types/body-parser": "1.17.0", - "@types/cors": "^2.8.4", - "@types/express": "4.16.0", - "accepts": "^1.3.5", - "apollo-server-core": "^2.1.0", - "body-parser": "^1.18.3", - "cors": "^2.8.4", - "graphql-subscriptions": "^0.5.8", - "graphql-tools": "^3.0.4", - "type-is": "^1.6.16" - }, - "dependencies": { - "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", - "requires": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" - } - }, - "apollo-link": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.3.tgz", - "integrity": "sha512-iL9yS2OfxYhigme5bpTbmRyC+Htt6tyo2fRMHT3K1XRL/C5IQDDz37OjpPy4ndx7WInSvfSZaaOTKFja9VWqSw==", - "requires": { - "apollo-utilities": "^1.0.0", - "zen-observable-ts": "^0.8.10" - } - }, - "body-parser": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", - "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", - "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", - "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", - "qs": "6.5.2", - "raw-body": "2.3.3", - "type-is": "~1.6.16" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "graphql-tools": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/graphql-tools/-/graphql-tools-3.1.1.tgz", - "integrity": "sha512-yHvPkweUB0+Q/GWH5wIG60bpt8CTwBklCSzQdEHmRUgAdEQKxw+9B7zB3dG7wB3Ym7M7lfrS4Ej+jtDZfA2UXg==", - "requires": { - "apollo-link": "^1.2.2", - "apollo-utilities": "^1.0.1", - "deprecated-decorator": "^0.1.6", - "iterall": "^1.1.3", - "uuid": "^3.1.0" - } - }, - "http-errors": { - "version": "1.6.3", - "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "mime-db": { - "version": "1.36.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", - "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==" - }, - "mime-types": { - "version": "2.1.20", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz", - "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==", - "requires": { - "mime-db": "~1.36.0" - } - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "raw-body": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", - "unpipe": "1.0.0" - } - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" - }, - "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.18" - } - } - } - }, - "apollo-tracing": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/apollo-tracing/-/apollo-tracing-0.2.5.tgz", - "integrity": "sha512-DZO7pfL5LATHeJdVFoTZ/N3HwA+IMf1YnIt5K+uMQW+/MrRgYOtTszUv5tYX2cUIqHYHcbdDaBQUuIXwSpaV2Q==", - "requires": { - "apollo-server-env": "^2.0.3", - "graphql-extensions": "^0.2.1" - } - }, - "apollo-utilities": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.0.13.tgz", - "integrity": "sha512-WIbKDsFsLXMgPPGmlB2pL2noHT13TOLU2eRSyPjQMQwcz9lO9UKRkwK8pRJ8b4Zo/9qQHm30F/xlfP/OSr2ZSw==" - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - }, - "dependencies": { - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - } - } - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1" - } - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "array-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", - "dev": true - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "array.prototype.find": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.0.4.tgz", - "integrity": "sha1-VWpcU2LAhkgyPdrrnenRS8GGTJA=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" - } - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" - }, - "assert-err": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assert-err/-/assert-err-1.1.0.tgz", - "integrity": "sha1-wFBieZodl9P16qJY4yQqq0mfyO8=", - "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" - }, - "async-retry": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.2.3.tgz", - "integrity": "sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q==", - "requires": { - "retry": "0.12.0" - } - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "attr-accept": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.0.tgz", - "integrity": "sha1-tc01In8WOTWo8d4Q7T66FpQfa+Y=" - }, - "autoprefixer": { - "version": "6.7.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", - "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", - "dev": true, - "requires": { - "browserslist": "^1.7.6", - "caniuse-db": "^1.0.30000634", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^5.2.16", - "postcss-value-parser": "^3.2.3" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "babel-core": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", - "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.0", - "debug": "^2.6.8", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.7", - "slash": "^1.0.0", - "source-map": "^0.5.6" - }, - "dependencies": { - "babel-register": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", - "dev": true, - "requires": { - "babel-core": "^6.26.0", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.15" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "^0.5.6" - } - } - } - }, - "babel-eslint": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-7.2.3.tgz", - "integrity": "sha1-sv4tgBJkcPXBlELcdXJTqJdxCCc=", - "dev": true, - "requires": { - "babel-code-frame": "^6.22.0", - "babel-traverse": "^6.23.1", - "babel-types": "^6.23.0", - "babylon": "^6.17.0" - } - }, - "babel-generator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", - "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", - "dev": true, - "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.6", - "trim-right": "^1.0.1" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "babel-helpers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-register": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.1.tgz", - "integrity": "sha1-fhDhOi9xBlvfrVoXh7pFvKbe118=", - "dev": true, - "requires": { - "babel-core": "^6.24.1", - "babel-runtime": "^6.22.0", - "core-js": "^2.4.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.2.0", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.2" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "^0.5.6" - } - } - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" - }, - "bail": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.2.tgz", - "integrity": "sha1-99bBcxYwqfnw1NNe0fli4gdKF2Q=" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "base64-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", - "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==" - }, - "batch": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.5.3.tgz", - "integrity": "sha1-PzQU84AyF0O/wQQvmoP/HVgk1GQ=" - }, - "bcrypt": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-2.0.1.tgz", - "integrity": "sha512-DwB7WgJPdskbR+9Y3OTJtwRq09Lmm7Na6b+4ewvXjkD0nfNRi1OozxljHm5ETlDCBq9DTy04lQz+rj+T2ztIJg==", - "requires": { - "nan": "2.10.0", - "node-pre-gyp": "0.9.1" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.0.1", - "bundled": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "bundled": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true - }, - "iconv-lite": { - "version": "0.4.21", - "bundled": true, - "requires": { - "safer-buffer": "^2.1.0" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true - }, - "ini": { - "version": "1.3.5", - "bundled": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true - }, - "minipass": { - "version": "2.2.4", - "bundled": true, - "requires": { - "safe-buffer": "^5.1.1", - "yallist": "^3.0.0" - }, - "dependencies": { - "yallist": { - "version": "3.0.2", - "bundled": true - } - } - }, - "minizlib": { - "version": "1.1.0", - "bundled": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true - }, - "needle": { - "version": "2.2.0", - "bundled": true, - "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.9.1", - "bundled": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.1.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.3", - "bundled": true - }, - "npm-packlist": { - "version": "1.1.10", - "bundled": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true - }, - "rc": { - "version": "1.2.6", - "bundled": true, - "requires": { - "deep-extend": "~0.4.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true - } - } - }, - "readable-stream": { - "version": "2.3.5", - "bundled": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.2", - "bundled": true, - "requires": { - "glob": "^7.0.5" - } - }, - "safe-buffer": { - "version": "5.1.1", - "bundled": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true - }, - "sax": { - "version": "1.2.4", - "bundled": true - }, - "semver": { - "version": "5.5.0", - "bundled": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.0.3", - "bundled": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true - }, - "tar": { - "version": "4.4.1", - "bundled": true, - "requires": { - "chownr": "^1.0.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.2.4", - "minizlib": "^1.1.0", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.1", - "yallist": "^3.0.2" - }, - "dependencies": { - "yallist": { - "version": "3.0.2", - "bundled": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "requires": { - "string-width": "^1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true - } - } - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "optional": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "bluebird": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=" - }, - "body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", - "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.1", - "http-errors": "~1.6.2", - "iconv-lite": "0.4.19", - "on-finished": "~2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "~1.6.15" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" - }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "requires": { - "hoek": "4.x.x" - } - }, - "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } - }, - "brcast": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/brcast/-/brcast-3.0.1.tgz", - "integrity": "sha512-eI3yqf9YEqyGl9PCNTR46MGvDylGtaHjalcz6Q3fAPnP/PhpKkkve52vFdfGpwp4VUvK6LUr4TQN+2stCrEwTg==" - }, - "browser-process-hrtime": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz", - "integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44=", - "dev": true - }, - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true, - "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" - } - }, - "buffer": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.8.tgz", - "integrity": "sha512-xXvjQhVNz50v2nPeoOsNqWCLGfiv4ji/gXZM28jnVwdLJxH4mFyqgqCKfaK9zf1KUbG6zTkjLOy7ou+jSMarGA==", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, - "buffer-from": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", - "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "busboy": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", - "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", - "requires": { - "dicer": "0.2.5", - "readable-stream": "1.1.x" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "^0.2.0" - } - }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" - }, - "caniuse-db": { - "version": "1.0.30000787", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000787.tgz", - "integrity": "sha1-ygeigb5Taoi9f6yWuolfPPU/gRs=", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - } - }, - "chain-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/chain-function/-/chain-function-1.0.0.tgz", - "integrity": "sha1-DUqzfn4Y6tC9xHuSB2QRjOWHM9w=" - }, - "chalk": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.2.0.tgz", - "integrity": "sha512-0BMM/2hG3ZaoPfR6F+h/oWpZtsh3b/s62TjSM6MGCJWEbJDN1acqCXvyhhZsDSVFklpebUoQ5O1kKC7lOzrn9g==", - "requires": { - "ansi-styles": "^3.1.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^4.0.0" - } - }, - "change-emitter": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz", - "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU=" - }, - "character-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.1.tgz", - "integrity": "sha1-92hxvl72bdt/j440eOzDdMJ9bco=" - }, - "character-entities-legacy": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.1.tgz", - "integrity": "sha1-9Ad53xoQGHK7UQo9KV4fzPFHIC8=" - }, - "character-reference-invalid": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.1.tgz", - "integrity": "sha1-lCg191Dk7GGjCOYMLvjMEBEgLvw=" - }, - "cheerio": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.19.0.tgz", - "integrity": "sha1-dy5wFfLuKZZQltcepBdbdas1SSU=", - "requires": { - "css-select": "~1.0.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "~3.8.1", - "lodash": "^3.2.0" - }, - "dependencies": { - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" - } - } - }, - "chromedriver": { - "version": "2.40.0", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-2.40.0.tgz", - "integrity": "sha512-ewvRQ1HMk0vpFSWYCk5hKDoEz5QMPplx5w3C6/Me+03y1imr67l3Hxl9U0jn3mu2N7+c7BoC7JtNW6HzbRAwDQ==", - "dev": true, - "requires": { - "del": "^3.0.0", - "extract-zip": "^1.6.7", - "kew": "^0.7.0", - "mkdirp": "^0.5.1", - "request": "^2.87.0" - }, - "dependencies": { - "del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", - "dev": true, - "requires": { - "globby": "^6.1.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "p-map": "^1.1.1", - "pify": "^3.0.0", - "rimraf": "^2.2.8" - } - }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "request": { - "version": "2.87.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", - "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", - "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", - "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "tough-cookie": "~2.3.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" - } - } - } - }, - "chrono-node": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/chrono-node/-/chrono-node-1.3.5.tgz", - "integrity": "sha1-oklSmKMtqCvMAa2b59d++l4kQSI=", - "requires": { - "moment": "^2.10.3" - } - }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, - "classnames": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz", - "integrity": "sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0=" - }, - "clean-css": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-1.1.7.tgz", - "integrity": "sha1-YB75z3ZCuYLLM+/JSIpkRMmGaG4=", - "requires": { - "commander": "2.0.x" - }, - "dependencies": { - "commander": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", - "integrity": "sha1-0bhvkB+LZL2UG96tr5JFMDk76Sg=" - } - } - }, - "cli-color": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-0.3.3.tgz", - "integrity": "sha1-EtW90Vj/igsNtAEZiRPAPfBp9vU=", - "requires": { - "d": "~0.1.1", - "es5-ext": "~0.10.6", - "memoizee": "~0.3.8", - "timers-ext": "0.1" - } - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "dev": true, - "requires": { - "restore-cursor": "^1.0.1" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "clipboard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.1.tgz", - "integrity": "sha512-7yhQBmtN+uYZmfRjjVjKa0dZdWuabzpSKGtyQZN+9C8xlC788SSJjOHWh7tzurfwTqTD5UDYAhIv5fRJg3sHjQ==", - "requires": { - "good-listener": "^1.2.2", - "select": "^1.1.2", - "tiny-emitter": "^2.0.0" - } - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" - } - } - }, - "clone": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", - "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=" - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "collapse-white-space": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.3.tgz", - "integrity": "sha1-S5BvZw5aljqHt2sOFolkM0G2Ajw=" - }, - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "requires": { - "color-name": "^1.1.1" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "colors": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", - "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=", - "dev": true - }, - "combined-stream": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", - "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==" - }, - "commonmark": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/commonmark/-/commonmark-0.28.1.tgz", - "integrity": "sha1-Buq41SM4uDn6Gi11rwCF7tGxvq4=", - "requires": { - "entities": "~ 1.1.1", - "mdurl": "~ 1.0.1", - "minimist": "~ 1.2.0", - "string.prototype.repeat": "^0.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - } - } - }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" - }, - "component-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-type/-/component-type-1.2.1.tgz", - "integrity": "sha1-ikeQFwAjjk/DIml3EjAibyS0Fak=" - }, - "compressible": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.12.tgz", - "integrity": "sha1-xZpcmdt2dn6YdlAOJx72OzSTvWY=", - "requires": { - "mime-db": ">= 1.30.0 < 2" - } - }, - "compression": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.1.tgz", - "integrity": "sha1-7/JgPvwuIs+G810uuTWJ+YdTc9s=", - "requires": { - "accepts": "~1.3.4", - "bytes": "3.0.0", - "compressible": "~2.0.11", - "debug": "2.6.9", - "on-headers": "~1.0.1", - "safe-buffer": "5.1.1", - "vary": "~1.1.2" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "concat-stream": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", - "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "convert-source-map": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", - "dev": true - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "cookie-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz", - "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=", - "requires": { - "cookie": "0.3.1", - "cookie-signature": "1.0.6" - } - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "cookiejar": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.1.tgz", - "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o=" - }, - "core-js": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", - "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cors": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", - "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "create-react-class": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.2.tgz", - "integrity": "sha1-zx7RXxKq1/FO9fLf4F5sQvke8Co=", - "requires": { - "fbjs": "^0.8.9", - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" - } - }, - "cross-fetch": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-0.0.8.tgz", - "integrity": "sha1-Ae2U3EB98sAPGAf95wCnz6SKIFw=", - "requires": { - "node-fetch": "1.7.3", - "whatwg-fetch": "2.0.3" - } - }, - "cross-spawn-async": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz", - "integrity": "sha1-hF/wwINKPe2dFg2sptOQkGuyiMw=", - "requires": { - "lru-cache": "^4.0.0", - "which": "^1.2.8" - } - }, - "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "requires": { - "boom": "5.x.x" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.x.x" - } - } - } - }, - "crypto-js": { - "version": "3.1.9-1", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz", - "integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg=" - }, - "crypto-token": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/crypto-token/-/crypto-token-1.0.1.tgz", - "integrity": "sha1-J8ZIL687Y8L12hFXf4MENG/nl6U=" - }, - "css-color-keywords": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", - "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=" - }, - "css-select": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.0.0.tgz", - "integrity": "sha1-sRIcpRhI3SZOIkTQWM7iVN7rRLA=", - "requires": { - "boolbase": "~1.0.0", - "css-what": "1.0", - "domutils": "1.4", - "nth-check": "~1.0.0" - } - }, - "css-to-react-native": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.0.4.tgz", - "integrity": "sha1-z0zEB1WLNHTUuovhos07bOcTEBs=", - "requires": { - "css-color-keywords": "^1.0.0", - "fbjs": "^0.8.5", - "postcss-value-parser": "^3.3.0" - } - }, - "css-vendor": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-0.3.8.tgz", - "integrity": "sha1-ZCHP0wNM5mT+dnOXL9ARn8KJQfo=", - "requires": { - "is-in-browser": "^1.0.2" - } - }, - "css-what": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-1.0.0.tgz", - "integrity": "sha1-18wt9FGAZm+Z0rFEYmOUaeAPc2w=" - }, - "cssom": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.0.tgz", - "integrity": "sha1-OG1RNVKP5lwe4bx8TlWjiFTbz3o=" - }, - "cssstyle": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.3.1.tgz", - "integrity": "sha512-tNvaxM5blOnxanyxI6panOsnfiyLRj3HV4qjqqS45WPNS1usdYWRUQjqTEEELK73lpeP/1KoIGYUwrBn/VcECA==", - "dev": true, - "requires": { - "cssom": "0.3.x" - } - }, - "csstype": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.5.7.tgz", - "integrity": "sha512-Nt5VDyOTIIV4/nRFswoCKps1R5CD1hkiyjBE9/thNaNZILLEviVw9yWQw15+O+CpNjQKB/uvdcxFFOrSflY3Yw==" - }, - "ctype": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", - "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=", - "optional": true - }, - "d": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", - "integrity": "sha1-2hhMU10Y2O57oqoim5FACfrhEwk=", - "requires": { - "es5-ext": "~0.10.2" - } - }, - "damerau-levenshtein": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz", - "integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ=", - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "data-urls": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.0.tgz", - "integrity": "sha512-ai40PPQR0Fn1lD2PPie79CibnlMN2AYiDhwFX/rZHVsxbs5kNJSjegqXIprhouGXlRdEnfybva7kqRGnB6mypA==", - "dev": true, - "requires": { - "abab": "^1.0.4", - "whatwg-mimetype": "^2.0.0", - "whatwg-url": "^6.4.0" - } - }, - "dataloader": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-1.3.0.tgz", - "integrity": "sha1-b+xb5LMKcS5K/TC4a0M0VmuXZzs=" - }, - "datauri": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/datauri/-/datauri-0.2.1.tgz", - "integrity": "sha1-9Oit27PlTj3BLRyIVDuLCxv2kvo=", - "requires": { - "mimer": "*", - "templayed": "*" - } - }, - "debounce": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz", - "integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg==" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" - }, - "deep-extend": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", - "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" - }, - "deepmerge": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", - "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==" - }, - "define-properties": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", - "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", - "requires": { - "foreach": "^2.0.5", - "object-keys": "^1.0.8" - } - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "delegate": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" - }, - "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" - }, - "deprecated-decorator": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz", - "integrity": "sha1-AJZjF7ehL+kvPMgx91g68ym4bDc=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - }, - "dicer": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", - "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", - "requires": { - "readable-stream": "1.1.x", - "streamsearch": "0.1.2" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "discontinuous-range": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", - "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=", - "dev": true - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-helpers": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.3.1.tgz", - "integrity": "sha512-2Sm+JaYn74OiTM2wHvxJOo3roiq/h25Yi69Fqk269cNUwIXsCvATB6CRSFC9Am/20G2b28hGv/+7NiWydIrPvg==" - }, - "dom-serializer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", - "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", - "requires": { - "domelementtype": "~1.1.1", - "entities": "~1.1.1" - }, - "dependencies": { - "domelementtype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", - "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=" - } - } - }, - "domelementtype": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", - "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=" - }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "dev": true, - "requires": { - "webidl-conversions": "^4.0.2" - } - }, - "domhandler": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", - "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.4.3.tgz", - "integrity": "sha1-CGVRN5bGswYDGFDhdVFrr4C3Km8=", - "requires": { - "domelementtype": "1" - } - }, - "dot-object": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/dot-object/-/dot-object-1.7.0.tgz", - "integrity": "sha512-X91KaxzQmhQvfokXCdM8EkJVDHe5HlffhT4eaS6CsLk8Nhs9ChA6KWmrt8kgCz5rmLKzoOEP2tmRK/kBvomFwg==", - "requires": { - "commander": "^2.10.0", - "glob": "^7.1.2" - } - }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "optional": true, - "requires": { - "jsbn": "~0.1.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "electron-releases": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/electron-releases/-/electron-releases-2.1.0.tgz", - "integrity": "sha512-cyKFD1bTE/UgULXfaueIN1k5EPFzs+FRc/rvCY5tIynefAPqopQEgjr0EzY+U3Dqrk/G4m9tXSPuZ77v6dL/Rw==", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.30", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.30.tgz", - "integrity": "sha512-zx1Prv7kYLfc4OA60FhxGbSo4qrEjgSzpo1/37i7l9ltXPYOoQBtjQxY9KmsgfHnBxHlBGXwLlsbt/gub1w5lw==", - "dev": true, - "requires": { - "electron-releases": "^2.1.0" - } - }, - "encodeurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" - }, - "encoding": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", - "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", - "requires": { - "iconv-lite": "~0.4.13" - } - }, - "entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", - "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=" - }, - "enzyme": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.3.0.tgz", - "integrity": "sha512-l8csyPyLmtxskTz6pX9W8eDOyH1ckEtDttXk/vlFWCjv00SkjTjtoUrogqp4yEvMyneU9dUJoOLnqFoiHb8IHA==", - "dev": true, - "requires": { - "cheerio": "^1.0.0-rc.2", - "function.prototype.name": "^1.0.3", - "has": "^1.0.1", - "is-boolean-object": "^1.0.0", - "is-callable": "^1.1.3", - "is-number-object": "^1.0.3", - "is-string": "^1.0.4", - "is-subset": "^0.1.1", - "lodash": "^4.17.4", - "object-inspect": "^1.5.0", - "object-is": "^1.0.1", - "object.assign": "^4.1.0", - "object.entries": "^1.0.4", - "object.values": "^1.0.4", - "raf": "^3.4.0", - "rst-selector-parser": "^2.2.3" - }, - "dependencies": { - "cheerio": { - "version": "1.0.0-rc.2", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz", - "integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=", - "dev": true, - "requires": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash": "^4.15.0", - "parse5": "^3.0.1" - } - }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "dev": true, - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "css-what": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", - "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", - "dev": true - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "htmlparser2": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", - "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", - "dev": true, - "requires": { - "domelementtype": "^1.3.0", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^2.0.2" - } - }, - "parse5": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", - "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", - "dev": true, - "requires": { - "@types/node": "*" - } - } - } - }, - "enzyme-adapter-react-16": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.1.1.tgz", - "integrity": "sha512-kC8pAtU2Jk3OJ0EG8Y2813dg9Ol0TXi7UNxHzHiWs30Jo/hj7alc//G1YpKUsPP1oKl9X+Lkx+WlGJpPYA+nvw==", - "dev": true, - "requires": { - "enzyme-adapter-utils": "^1.3.0", - "lodash": "^4.17.4", - "object.assign": "^4.0.4", - "object.values": "^1.0.4", - "prop-types": "^15.6.0", - "react-reconciler": "^0.7.0", - "react-test-renderer": "^16.0.0-0" - } - }, - "enzyme-adapter-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.4.0.tgz", - "integrity": "sha512-ajvyXQYbmCoKCX/FaraNzBgXDXJBltCd0GdXfKc0DdRPYgCLaZfS6Ts576IFt8aX2GU9ajZv2g5jfcJ+Nttejw==", - "dev": true, - "requires": { - "object.assign": "^4.1.0", - "prop-types": "^15.6.0" - } - }, - "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.10.0.tgz", - "integrity": "sha512-/uh/DhdqIOSkAWifU+8nG78vlQxdLckUdI/sPgy0VhuXi2qJ7T8czBmqIYtLQVpCIFYafChnsRsB5pyb1JdmCQ==", - "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" - } - }, - "es-to-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", - "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", - "requires": { - "is-callable": "^1.1.1", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" - } - }, - "es5-ext": { - "version": "0.10.37", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.37.tgz", - "integrity": "sha1-DudB0Ui4AGm6J9AgOTdWryV978M=", - "requires": { - "es6-iterator": "~2.0.1", - "es6-symbol": "~3.1.1" - } - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - }, - "dependencies": { - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "requires": { - "es5-ext": "^0.10.9" - } - } - } - }, - "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-set": "~0.1.5", - "es6-symbol": "~3.1.1", - "event-emitter": "~0.3.5" - }, - "dependencies": { - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dev": true, - "requires": { - "es5-ext": "^0.10.9" - } - } - } - }, - "es6-promise": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", - "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=", - "dev": true - }, - "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-symbol": "3.1.1", - "event-emitter": "~0.3.5" - }, - "dependencies": { - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dev": true, - "requires": { - "es5-ext": "^0.10.9" - } - } - } - }, - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - }, - "dependencies": { - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "requires": { - "es5-ext": "^0.10.9" - } - } - } - }, - "es6-weak-map": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-0.1.4.tgz", - "integrity": "sha1-cGzvnpmqI2undmwjnIueKG6n0ig=", - "requires": { - "d": "~0.1.1", - "es5-ext": "~0.10.6", - "es6-iterator": "~0.1.3", - "es6-symbol": "~2.0.1" - }, - "dependencies": { - "es6-iterator": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-0.1.3.tgz", - "integrity": "sha1-1vWLjE/EE8JJtLqhl2j45NfIlE4=", - "requires": { - "d": "~0.1.1", - "es5-ext": "~0.10.5", - "es6-symbol": "~2.0.1" - } - }, - "es6-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-2.0.1.tgz", - "integrity": "sha1-dhtcZ8/U8dGK+yNPaR1nhoLLO/M=", - "requires": { - "d": "~0.1.1", - "es5-ext": "~0.10.5" - } - } - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-regexp-component": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/escape-regexp-component/-/escape-regexp-component-1.0.2.tgz", - "integrity": "sha1-nGO20LJf8qiMOtvRjFthrMO5+qI=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "escodegen": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz", - "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", - "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - } - }, - "escope": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", - "dev": true, - "requires": { - "es6-map": "^0.1.3", - "es6-weak-map": "^2.0.1", - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - }, - "dependencies": { - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dev": true, - "requires": { - "es5-ext": "^0.10.9" - } - }, - "es6-weak-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.14", - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" - } - } - } - }, - "eslint": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", - "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", - "dev": true, - "requires": { - "babel-code-frame": "^6.16.0", - "chalk": "^1.1.3", - "concat-stream": "^1.5.2", - "debug": "^2.1.1", - "doctrine": "^2.0.0", - "escope": "^3.6.0", - "espree": "^3.4.0", - "esquery": "^1.0.0", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "glob": "^7.0.3", - "globals": "^9.14.0", - "ignore": "^3.2.0", - "imurmurhash": "^0.1.4", - "inquirer": "^0.12.0", - "is-my-json-valid": "^2.10.0", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.5.1", - "json-stable-stringify": "^1.0.0", - "levn": "^0.3.0", - "lodash": "^4.0.0", - "mkdirp": "^0.5.0", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.1", - "pluralize": "^1.2.1", - "progress": "^1.1.8", - "require-uncached": "^1.0.2", - "shelljs": "^0.7.5", - "strip-bom": "^3.0.0", - "strip-json-comments": "~2.0.1", - "table": "^3.7.8", - "text-table": "~0.2.0", - "user-home": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "eslint-config-airbnb": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-13.0.0.tgz", - "integrity": "sha1-aI0V08J2wMdTrlOMkqRDl9dq5G4=", - "dev": true, - "requires": { - "eslint-config-airbnb-base": "^10.0.0" - } - }, - "eslint-config-airbnb-base": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-10.0.1.tgz", - "integrity": "sha1-8X1OUpksHUXRt3E++81ezQ5+BQY=", - "dev": true - }, - "eslint-config-meteor": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/eslint-config-meteor/-/eslint-config-meteor-0.0.9.tgz", - "integrity": "sha1-a+IZQguko+oCPbMKhm5g70h2Uvo=", - "dev": true - }, - "eslint-import-resolver-meteor": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-meteor/-/eslint-import-resolver-meteor-0.3.4.tgz", - "integrity": "sha1-40ulNL+ZVCDrTNIjMC56WWfu5Tk=", - "dev": true, - "requires": { - "object-assign": "^4.0.1", - "resolve": "^1.1.6" - } - }, - "eslint-import-resolver-node": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.1.tgz", - "integrity": "sha512-yUtXS15gIcij68NmXmP9Ni77AQuCN0itXbCc/jWd8C6/yKZaSNXicpC8cgvjnxVdmfsosIXrjpzFq7GcDryb6A==", - "dev": true, - "requires": { - "debug": "^2.6.8", - "resolve": "^1.2.0" - } - }, - "eslint-module-utils": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz", - "integrity": "sha512-jDI/X5l/6D1rRD/3T43q8Qgbls2nq5km5KSqiwlyUbGo5+04fXhMKdCPhjwbqAa6HXWaMxj8Q4hQDIh7IadJQw==", - "dev": true, - "requires": { - "debug": "^2.6.8", - "pkg-dir": "^1.0.0" - } - }, - "eslint-plugin-babel": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-babel/-/eslint-plugin-babel-3.3.0.tgz", - "integrity": "sha1-L0lK7c9vSqTnW5FVmAg3vB+94ZM=", - "dev": true - }, - "eslint-plugin-import": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.8.0.tgz", - "integrity": "sha512-Rf7dfKJxZ16QuTgVv1OYNxkZcsu/hULFnC+e+w0Gzi6jMC3guQoWQgxYxc54IDRinlb6/0v5z/PxxIKmVctN+g==", - "dev": true, - "requires": { - "builtin-modules": "^1.1.1", - "contains-path": "^0.1.0", - "debug": "^2.6.8", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.1", - "eslint-module-utils": "^2.1.1", - "has": "^1.0.1", - "lodash.cond": "^4.3.0", - "minimatch": "^3.0.3", - "read-pkg-up": "^2.0.0" - }, - "dependencies": { - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - } - } - }, - "eslint-plugin-jsx-a11y": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-2.2.3.tgz", - "integrity": "sha1-TjXLcbin23AqxBXIBuuOjZ6mxl0=", - "dev": true, - "requires": { - "damerau-levenshtein": "^1.0.0", - "jsx-ast-utils": "^1.0.0", - "object-assign": "^4.0.1" - } - }, - "eslint-plugin-meteor": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-meteor/-/eslint-plugin-meteor-4.1.4.tgz", - "integrity": "sha512-XBCD5oOYlDKiWqMEgO7fGXY9hJcUZMxkr8MLRtQ2XjtcJxfvdtgB3Dqn9aZLtNkg9qZoq67gN30Oirhn0mUEHg==", - "dev": true, - "requires": { - "babel-register": "6.24.1", - "babel-runtime": "6.25.0", - "escope": "3.6.0", - "invariant": "2.2.2", - "lodash.find": "4.6.0", - "lodash.memoize": "4.1.2", - "path-exists": "3.0.0" - }, - "dependencies": { - "babel-runtime": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.25.0.tgz", - "integrity": "sha1-M7mOql1IK7AajRqmtDetKwGuxBw=", - "dev": true, - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.10.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "regenerator-runtime": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", - "dev": true - } - } - }, - "eslint-plugin-mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-5.2.0.tgz", - "integrity": "sha512-4VTX/qIoxUFRnXLNm6bEhEJyfGnGagmQzV4TWXKzkZgIYyP2FSubEdCjEFTyS/dGwSVRWCWGX7jO7BK8R0kppg==", - "dev": true, - "requires": { - "ramda": "^0.25.0" - } - }, - "eslint-plugin-prettier": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.5.0.tgz", - "integrity": "sha512-L06bewYpt2Wb8Uk7os8f/0cL5DjddL38t1M/nOpjw5MqVFBn1RIIBBE6tfr37lHUH7AvAubZsvu/bDmNl4RBKQ==", - "dev": true, - "requires": { - "fast-diff": "^1.1.1", - "jest-docblock": "^21.0.0" - } - }, - "eslint-plugin-react": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz", - "integrity": "sha1-xUNb6wZ3ThLH2y9qut3L+QDNP3g=", - "dev": true, - "requires": { - "array.prototype.find": "^2.0.1", - "doctrine": "^1.2.2", - "has": "^1.0.1", - "jsx-ast-utils": "^1.3.4", - "object.assign": "^4.0.4" - }, - "dependencies": { - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - } - } - }, - "espree": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", - "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", - "dev": true, - "requires": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" - } - }, - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" - }, - "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", - "dev": true, - "requires": { - "estraverse": "^4.0.0" - } - }, - "esrecurse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", - "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", - "dev": true, - "requires": { - "estraverse": "^4.1.0", - "object-assign": "^4.0.1" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - }, - "dependencies": { - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "requires": { - "es5-ext": "^0.10.9" - } - } - } - }, - "eventemitter3": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==" - }, - "exenv": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", - "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=" - }, - "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", - "dev": true - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "^2.1.0" - } - }, - "expect": { - "version": "23.4.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-23.4.0.tgz", - "integrity": "sha1-baTsyZwUcSU+cogziYOtHrrbYMM=", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "jest-diff": "^23.2.0", - "jest-get-type": "^22.1.0", - "jest-matcher-utils": "^23.2.0", - "jest-message-util": "^23.4.0", - "jest-regex-util": "^23.3.0" - } - }, - "express": { - "version": "4.16.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", - "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", - "requires": { - "accepts": "~1.3.4", - "array-flatten": "1.1.1", - "body-parser": "1.18.2", - "content-disposition": "0.5.2", - "content-type": "~1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.1", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.1.0", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.2", - "qs": "6.5.1", - "range-parser": "~1.2.0", - "safe-buffer": "5.1.1", - "send": "0.16.1", - "serve-static": "1.13.1", - "setprototypeof": "1.1.0", - "statuses": "~1.3.1", - "type-is": "~1.6.15", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" - }, - "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" - } - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "extract-zip": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", - "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", - "dev": true, - "requires": { - "concat-stream": "1.6.2", - "debug": "2.6.9", - "mkdirp": "0.5.1", - "yauzl": "2.4.1" - }, - "dependencies": { - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" - }, - "fast-diff": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", - "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, - "fbjs": { - "version": "0.8.16", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz", - "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=", - "requires": { - "core-js": "^1.0.0", - "isomorphic-fetch": "^2.1.1", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.9" - }, - "dependencies": { - "core-js": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", - "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" - } - } - }, - "fbjs-css-vars": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.1.tgz", - "integrity": "sha512-IM+v/C40MNZWqsLErc32e0TyIk/NhkkQZL0QmjBh6zi1eXv0/GeVKmKmueQX7nn9SXQBQbTUcB8zuexIF3/88w==" - }, - "fclone": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz", - "integrity": "sha1-EOhdo4v+p/xZk0HClu4ddyZu5kA=" - }, - "fd-slicer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", - "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", - "dev": true, - "requires": { - "pend": "~1.2.0" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - } - }, - "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "dev": true, - "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" - } - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, - "fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", - "dev": true, - "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.3.1", - "unpipe": "~1.0.0" - }, - "dependencies": { - "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" - } - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "flat": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.0.0.tgz", - "integrity": "sha512-ji/WMv2jdsE+LaznpkIF9Haax0sdpTBozrz/Dtg4qSRMfbs8oVg4ypJunIRYPiMLvH/ed6OflXbnbTIKJhtgeg==", - "requires": { - "is-buffer": "~1.1.5" - } - }, - "flat-cache": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", - "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", - "dev": true, - "requires": { - "circular-json": "^0.3.1", - "del": "^2.0.2", - "graceful-fs": "^4.1.2", - "write": "^0.2.1" - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", - "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.5", - "mime-types": "^2.1.12" - } - }, - "form-data-to-object": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/form-data-to-object/-/form-data-to-object-0.2.0.tgz", - "integrity": "sha1-96jmjd2RChEApl4lrGpIQUP/gWg=" - }, - "formidable": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.1.1.tgz", - "integrity": "sha1-lriIb3w8NQi5Mta9cMTTqI818ak=" - }, - "formsy-react": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/formsy-react/-/formsy-react-0.19.5.tgz", - "integrity": "sha1-dgpXrAETRC499MMJw2ON2SlX544=", - "requires": { - "form-data-to-object": "^0.2.0" - } - }, - "formsy-react-components": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/formsy-react-components/-/formsy-react-components-0.11.1.tgz", - "integrity": "sha1-ucMGytfeQR92UaVsd1XY9Oabm5M=", - "requires": { - "classnames": "^2.1.3", - "prop-types": "^15.5.7" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "function.prototype.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.0.tgz", - "integrity": "sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "is-callable": "^1.1.3" - } - }, - "generate-function": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", - "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", - "dev": true, - "requires": { - "is-property": "^1.0.2" - } - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "dev": true, - "requires": { - "is-property": "^1.0.0" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "^2.0.0" - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "good-listener": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", - "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", - "requires": { - "delegate": "^3.1.2" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" - }, - "graphql": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.10.5.tgz", - "integrity": "sha512-Q7cx22DiLhwHsEfUnUip1Ww/Vfx7FS0w6+iHItNuN61+XpegHSa3k5U0+6M5BcpavQImBwFiy0z3uYwY7cXMLQ==", - "requires": { - "iterall": "^1.1.0" - } - }, - "graphql-anywhere": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/graphql-anywhere/-/graphql-anywhere-3.1.0.tgz", - "integrity": "sha1-PqDY6GRrXO5oA1AWqadVfBXCHpY=" - }, - "graphql-date": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/graphql-date/-/graphql-date-1.0.3.tgz", - "integrity": "sha1-Mc4FrkDtjIzrBANkBgEJdx5xLpE=", - "requires": { - "assert-err": "^1.0.0" - } - }, - "graphql-extensions": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/graphql-extensions/-/graphql-extensions-0.2.1.tgz", - "integrity": "sha512-/1FTPSWSffDjlRyMAV2UwQhojLmca9aQD0ieo1IYiqT5SE+uOWi4r83QF1CoER0sREIsH3s+nTmdH3cvQVG3MA==", - "requires": { - "apollo-server-env": "^2.0.3" - } - }, - "graphql-subscriptions": { - "version": "0.5.8", - "resolved": "http://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-0.5.8.tgz", - "integrity": "sha512-0CaZnXKBw2pwnIbvmVckby5Ge5e2ecmjofhYCdyeACbCly2j3WXDP/pl+s+Dqd2GQFC7y99NB+53jrt55CKxYQ==", - "requires": { - "iterall": "^1.2.1" - }, - "dependencies": { - "iterall": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz", - "integrity": "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==" - } - } - }, - "graphql-tag": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.6.1.tgz", - "integrity": "sha1-R4jVCfbilgfZR/xHpAxOGPc200o=" - }, - "graphql-type-json": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/graphql-type-json/-/graphql-type-json-0.1.4.tgz", - "integrity": "sha1-ifE/XTLOCMmnbHn9+cGWg4TYGk4=" - }, - "graphql-voyager": { - "version": "1.0.0-rc.26", - "resolved": "https://registry.npmjs.org/graphql-voyager/-/graphql-voyager-1.0.0-rc.26.tgz", - "integrity": "sha512-ga4LNHivG1Zym0mYzbKUPfsZuPhmpbiU6TqyfL0UHy14AzfDzNd/tjzSRBPrrnFO07vV55wl/UuEk1ny2Z78IA==", - "requires": { - "@f/animate": "^1.0.1", - "@material-ui/core": "^3.0.2", - "classnames": "^2.2.6", - "clipboard": "^2.0.1", - "commonmark": "^0.28.1", - "lodash": "^4.17.10", - "prop-types": "^15.6.2", - "react-redux": "^5.0.7", - "redux": "^4.0.0", - "reselect": "^4.0.0", - "svg-pan-zoom": "^3.5.2", - "viz.js": "2.0.0" - }, - "dependencies": { - "classnames": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", - "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" - }, - "hoist-non-react-statics": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", - "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" - }, - "lodash-es": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz", - "integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==" - }, - "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", - "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" - } - }, - "react-redux": { - "version": "5.0.7", - "resolved": "http://registry.npmjs.org/react-redux/-/react-redux-5.0.7.tgz", - "integrity": "sha512-5VI8EV5hdgNgyjfmWzBbdrqUkrVRKlyTKk1sGH3jzM2M2Mhj/seQgPXaz6gVAj2lz/nz688AdTqMO18Lr24Zhg==", - "requires": { - "hoist-non-react-statics": "^2.5.0", - "invariant": "^2.0.0", - "lodash": "^4.17.5", - "lodash-es": "^4.17.5", - "loose-envify": "^1.1.0", - "prop-types": "^15.6.0" - } - }, - "redux": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.0.tgz", - "integrity": "sha512-NnnHF0h0WVE/hXyrB6OlX67LYRuaf/rJcbWvnHHEPCF/Xa/AZpwhs/20WyqzQae5x4SD2F9nPObgBh2rxAgLiA==", - "requires": { - "loose-envify": "^1.1.0", - "symbol-observable": "^1.2.0" - } - }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" - } - } - }, - "handlebars": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", - "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", - "requires": { - "async": "^1.4.0", - "optimist": "^0.6.1", - "source-map": "^0.4.4", - "uglify-js": "^2.6" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" - }, - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "requires": { - "ajv": "^5.1.0", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", - "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", - "requires": { - "function-bind": "^1.0.2" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" - }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true - }, - "hash.js": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz", - "integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==", - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "requires": { - "boom": "4.x.x", - "cryptiles": "3.x.x", - "hoek": "4.x.x", - "sntp": "2.x.x" - } - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" - }, - "history": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/history/-/history-3.3.0.tgz", - "integrity": "sha1-/O3M6PEpdTcVRdc1RhAzV5ptrpw=", - "requires": { - "invariant": "^2.2.1", - "loose-envify": "^1.2.0", - "query-string": "^4.2.2", - "warning": "^3.0.0" - } - }, - "hoek": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", - "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==" - }, - "hoist-non-react-statics": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz", - "integrity": "sha1-ND24TGAYxlB3iJgkATWhQg7iLOA=" - }, - "home-or-tmp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" - } - }, - "hosted-git-info": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", - "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", - "dev": true - }, - "html-encoding-sniffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.1" - } - }, - "html-to-text": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-2.1.3.tgz", - "integrity": "sha1-5Q2+TkB5kS2+3N4F0ng4jtuO6pE=", - "requires": { - "he": "^1.0.0", - "htmlparser": "^1.7.7", - "optimist": "^0.6.1", - "underscore": "^1.8.3", - "underscore.string": "^3.2.3" - } - }, - "htmlparser": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/htmlparser/-/htmlparser-1.7.7.tgz", - "integrity": "sha1-GeezmX/2+6yZrlp9J2ZInv5+LQ4=" - }, - "htmlparser2": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", - "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", - "requires": { - "domelementtype": "1", - "domhandler": "2.3", - "domutils": "1.5", - "entities": "1.0", - "readable-stream": "1.1" - }, - "dependencies": { - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "entities": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", - "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=" - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "requires": { - "depd": "1.1.1", - "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": ">= 1.3.1 < 2" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "hyphenate-style-name": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz", - "integrity": "sha1-MRYKNpMK2vH8BMYHT360FGXU7Es=" - }, - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" - }, - "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" - }, - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", - "dev": true - }, - "immutability-helper": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-2.6.2.tgz", - "integrity": "sha512-2boGYtiA++szFIXl9tlTJ7f2emec+3UCaRjv8Ixnw62G43rz2n+vZ5kfs6fUmCORKuhJHFR2oSuAxBgrMxt95g==", - "requires": { - "invariant": "^2.2.0" - } - }, - "import": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/import/-/import-0.0.6.tgz", - "integrity": "sha1-0Ot534aqJnfG22FXilISswMeYEI=", - "requires": { - "optimist": "0.3.x" - }, - "dependencies": { - "optimist": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", - "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", - "requires": { - "wordwrap": "~0.0.2" - } - } - } - }, - "import-inspector": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-inspector/-/import-inspector-2.0.0.tgz", - "integrity": "sha1-znX9tqJ30oAO/+CX4ilbyGkLKSM=" - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indefinite-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indefinite-observable/-/indefinite-observable-1.0.1.tgz", - "integrity": "sha1-CZFUI8yNb36xy3iCrRNGM8mm7cM=", - "requires": { - "symbol-observable": "1.0.4" - }, - "dependencies": { - "symbol-observable": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz", - "integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0=" - } - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "inquirer": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", - "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", - "dev": true, - "requires": { - "ansi-escapes": "^1.1.0", - "ansi-regex": "^2.0.0", - "chalk": "^1.0.0", - "cli-cursor": "^1.0.1", - "cli-width": "^2.0.0", - "figures": "^1.3.5", - "lodash": "^4.3.0", - "readline2": "^1.0.1", - "run-async": "^0.1.0", - "rx-lite": "^3.1.2", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", - "dev": true - }, - "intl": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/intl/-/intl-1.2.5.tgz", - "integrity": "sha1-giRKIZDE5Bn4Nx9ao02qNCDiq94=" - }, - "intl-format-cache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/intl-format-cache/-/intl-format-cache-2.1.0.tgz", - "integrity": "sha1-BKNp/sv61tpgBbrh8UMzMy3PkxY=" - }, - "intl-locales-supported": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/intl-locales-supported/-/intl-locales-supported-1.0.0.tgz", - "integrity": "sha1-mp2U2/EEqHgYiBlS3LeCBT8K7vo=" - }, - "intl-messageformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-2.2.0.tgz", - "integrity": "sha1-NFvNRt5jC3aDMwwuUhd/9eq0hPw=", - "requires": { - "intl-messageformat-parser": "1.4.0" - } - }, - "intl-messageformat-parser": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz", - "integrity": "sha1-tD1FqXRoytvkQzHXS7Ho3qRPwHU=" - }, - "intl-relativeformat": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/intl-relativeformat/-/intl-relativeformat-2.1.0.tgz", - "integrity": "sha1-AQ8RBYAiUfQKxH0OPhogE0iiVd8=", - "requires": { - "intl-messageformat": "^2.0.0" - } - }, - "invariant": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", - "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", - "requires": { - "loose-envify": "^1.0.0" - } - }, - "ipaddr.js": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", - "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" - }, - "is-alphabetical": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.1.tgz", - "integrity": "sha1-x3B5zJHU76x3W+EDS/LSQ/lebwg=" - }, - "is-alphanumerical": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.1.tgz", - "integrity": "sha1-37SqTRCF4zvbYcLe6cgOnGwZ9Ts=", - "requires": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-boolean-object": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.0.tgz", - "integrity": "sha1-mPiygDBoQhmpXzdc+9iM40Bd/5M=", - "dev": true - }, - "is-buffer": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", - "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=" - }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true, - "requires": { - "builtin-modules": "^1.0.0" - } - }, - "is-callable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", - "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=" - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" - }, - "is-decimal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.1.tgz", - "integrity": "sha1-9ftqlJlq2ejjdh+/vQkfH8qMToI=" - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "requires": { - "is-primitive": "^2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "is-hexadecimal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.1.tgz", - "integrity": "sha1-bghLvJIGH7sJcexYts5tQE4k2mk=" - }, - "is-in-browser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", - "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=" - }, - "is-isodate": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/is-isodate/-/is-isodate-0.0.1.tgz", - "integrity": "sha1-T+LpN9UPO6aMe2mwIYAItiSqUDY=" - }, - "is-my-ip-valid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", - "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", - "dev": true - }, - "is-my-json-valid": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.19.0.tgz", - "integrity": "sha512-mG0f/unGX1HZ5ep4uhRaPOS8EkAY8/j6mDRMJrutq4CqhoJWYp7qAlonIPy3TV7p3ju4TK9fo/PbnoksWmsp5Q==", - "dev": true, - "requires": { - "generate-function": "^2.0.0", - "generate-object-property": "^1.1.0", - "is-my-ip-valid": "^1.0.0", - "jsonpointer": "^4.0.0", - "xtend": "^4.0.0" - } - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-number-object": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.3.tgz", - "integrity": "sha1-8mWrian0RQNO9q/xWo8AsA9VF5k=", - "dev": true - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", - "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", - "dev": true, - "requires": { - "is-path-inside": "^1.0.0" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "requires": { - "isobject": "^3.0.1" - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "dev": true - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "requires": { - "has": "^1.0.1" - } - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-string": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.4.tgz", - "integrity": "sha1-zDqbaYV9Yh6WNyWiTK7shzuCbmQ=", - "dev": true - }, - "is-subset": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", - "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", - "dev": true - }, - "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-url": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.2.tgz", - "integrity": "sha1-SYkFpZO/R8wtnn9zg3K792lsfyY=" - }, - "is-webpack-bundle": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-webpack-bundle/-/is-webpack-bundle-1.0.0.tgz", - "integrity": "sha1-V2pQu3xT0dalwWR5Ock64su5Duo=" - }, - "is-whitespace-character": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.1.tgz", - "integrity": "sha1-muAXbzKCtlRXoZks2whPil+DPjs=" - }, - "is-word-character": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.1.tgz", - "integrity": "sha1-WgP6HqkazopusMfNdw64bWXIvvs=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "isomorphic-fetch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", - "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", - "requires": { - "node-fetch": "^1.0.1", - "whatwg-fetch": ">=0.10.0" - } - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "iterall": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.1.3.tgz", - "integrity": "sha512-Cu/kb+4HiNSejAPhSaN1VukdNTTi/r4/e+yykqjlG/IW+1gZH5b4+Bq3whDX4tvbYugta3r8KTMUiqT3fIGxuQ==" - }, - "jest-diff": { - "version": "23.2.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-23.2.0.tgz", - "integrity": "sha1-nyz0tR4Sx5FVAgCrwWtHEwrxBio=", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "diff": "^3.2.0", - "jest-get-type": "^22.1.0", - "pretty-format": "^23.2.0" - } - }, - "jest-docblock": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.2.0.tgz", - "integrity": "sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw==", - "dev": true - }, - "jest-get-type": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", - "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", - "dev": true - }, - "jest-matcher-utils": { - "version": "23.2.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-23.2.0.tgz", - "integrity": "sha1-TUmB8jIT6Tnjzt8j3DTHR7WuGRM=", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-get-type": "^22.1.0", - "pretty-format": "^23.2.0" - } - }, - "jest-message-util": { - "version": "23.4.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.4.0.tgz", - "integrity": "sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8=", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0-beta.35", - "chalk": "^2.0.1", - "micromatch": "^2.3.11", - "slash": "^1.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-regex-util": { - "version": "23.3.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-23.3.0.tgz", - "integrity": "sha1-X4ZylUfCeFxAAs6qj4Sf6MpHG8U=", - "dev": true - }, - "join-component": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/join-component/-/join-component-1.1.0.tgz", - "integrity": "sha1-uEF7dQZho5K+4sJTfGiyqdSXfNU=" - }, - "js-base64": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.0.tgz", - "integrity": "sha512-Wehd+7Pf9tFvGb+ydPm9TjYjV8X1YHOVyG8QyELZxEMqOhemVwGRmoG8iQ/soqI3n8v4xn59zaLxiCJiaaRzKA==", - "dev": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" - }, - "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "dependencies": { - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - } - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "optional": true - }, - "jsdom": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.11.0.tgz", - "integrity": "sha512-ou1VyfjwsSuWkudGxb03FotDajxAto6USAlmMZjE2lc0jCznt7sBWkhfRBRaWwbnmDqdMSTKTLT5d9sBFkkM7A==", - "dev": true, - "requires": { - "abab": "^1.0.4", - "acorn": "^5.3.0", - "acorn-globals": "^4.1.0", - "array-equal": "^1.0.0", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": ">= 0.3.1 < 0.4.0", - "data-urls": "^1.0.0", - "domexception": "^1.0.0", - "escodegen": "^1.9.0", - "html-encoding-sniffer": "^1.0.2", - "left-pad": "^1.2.0", - "nwsapi": "^2.0.0", - "parse5": "4.0.0", - "pn": "^1.1.0", - "request": "^2.83.0", - "request-promise-native": "^1.0.5", - "sax": "^1.2.4", - "symbol-tree": "^3.2.2", - "tough-cookie": "^2.3.3", - "w3c-hr-time": "^1.0.1", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.3", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^6.4.1", - "ws": "^4.0.0", - "xml-name-validator": "^3.0.0" - }, - "dependencies": { - "cssom": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz", - "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==", - "dev": true - } - } - }, - "jsdom-global": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsdom-global/-/jsdom-global-3.0.2.tgz", - "integrity": "sha1-a9KZwTsMRiay2iwDk81DhdYGrLk=", - "dev": true - }, - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "requires": { - "jsonify": "~0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "jss": { - "version": "9.8.7", - "resolved": "https://registry.npmjs.org/jss/-/jss-9.8.7.tgz", - "integrity": "sha512-awj3XRZYxbrmmrx9LUSj5pXSUfm12m8xzi/VKeqI1ZwWBtQ0kVPTs3vYs32t4rFw83CgFDukA8wKzOE9sMQnoQ==", - "requires": { - "is-in-browser": "^1.1.3", - "symbol-observable": "^1.1.0", - "warning": "^3.0.0" - } - }, - "jss-camel-case": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jss-camel-case/-/jss-camel-case-6.1.0.tgz", - "integrity": "sha512-HPF2Q7wmNW1t79mCqSeU2vdd/vFFGpkazwvfHMOhPlMgXrJDzdj9viA2SaHk9ZbD5pfL63a8ylp4++irYbbzMQ==", - "requires": { - "hyphenate-style-name": "^1.0.2" - } - }, - "jss-default-unit": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/jss-default-unit/-/jss-default-unit-8.0.2.tgz", - "integrity": "sha512-WxNHrF/18CdoAGw2H0FqOEvJdREXVXLazn7PQYU7V6/BWkCV0GkmWsppNiExdw8dP4TU1ma1dT9zBNJ95feLmg==" - }, - "jss-global": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jss-global/-/jss-global-3.0.0.tgz", - "integrity": "sha512-wxYn7vL+TImyQYGAfdplg7yaxnPQ9RaXY/cIA8hawaVnmmWxDHzBK32u1y+RAvWboa3lW83ya3nVZ/C+jyjZ5Q==" - }, - "jss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jss-nested/-/jss-nested-6.0.1.tgz", - "integrity": "sha512-rn964TralHOZxoyEgeq3hXY8hyuCElnvQoVrQwKHVmu55VRDd6IqExAx9be5HgK0yN/+hQdgAXQl/GUrBbbSTA==", - "requires": { - "warning": "^3.0.0" - } - }, - "jss-props-sort": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/jss-props-sort/-/jss-props-sort-6.0.0.tgz", - "integrity": "sha512-E89UDcrphmI0LzmvYk25Hp4aE5ZBsXqMWlkFXS0EtPkunJkRr+WXdCNYbXbksIPnKlBenGB9OxzQY+mVc70S+g==" - }, - "jss-vendor-prefixer": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/jss-vendor-prefixer/-/jss-vendor-prefixer-7.0.0.tgz", - "integrity": "sha512-Agd+FKmvsI0HLcYXkvy8GYOw3AAASBUpsmIRvVQheps+JWaN892uFOInTr0DRydwaD91vSSUCU4NssschvF7MA==", - "requires": { - "css-vendor": "^0.3.8" - } - }, - "jsx-ast-utils": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", - "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=", - "dev": true - }, - "jszip": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz", - "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==", - "dev": true, - "requires": { - "core-js": "~2.3.0", - "es6-promise": "~3.0.2", - "lie": "~3.1.0", - "pako": "~1.0.2", - "readable-stream": "~2.0.6" - }, - "dependencies": { - "core-js": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", - "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=", - "dev": true - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "juice": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/juice/-/juice-1.11.0.tgz", - "integrity": "sha1-Sn/2vvPz+kthD4JK6yftjzYBrXM=", - "requires": { - "batch": "0.5.3", - "cheerio": "0.19.0", - "commander": "2.9.0", - "cross-spawn-async": "^2.1.8", - "cssom": "0.3.0", - "deep-extend": "^0.4.0", - "slick": "1.12.2", - "util-deprecate": "^1.0.2", - "web-resource-inliner": "1.2.1" - }, - "dependencies": { - "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", - "requires": { - "graceful-readlink": ">= 1.0.0" - } - } - } - }, - "kew": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", - "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", - "dev": true - }, - "keycode": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.1.9.tgz", - "integrity": "sha1-lkojxU5IiUBbSGGlyfBIDUUUHfo=" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" - }, - "left-pad": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", - "dev": true, - "requires": { - "immediate": "~3.0.5" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "dependencies": { - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" - }, - "lodash-es": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.4.tgz", - "integrity": "sha1-3MHXVS4VCgZABzupyzHXDwMpUOc=" - }, - "lodash._basecallback": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/lodash._basecallback/-/lodash._basecallback-3.3.1.tgz", - "integrity": "sha1-t7K7Q9whYEJKIczybFfkQ3cqjic=", - "requires": { - "lodash._baseisequal": "^3.0.0", - "lodash._bindcallback": "^3.0.0", - "lodash.isarray": "^3.0.0", - "lodash.pairs": "^3.0.0" - } - }, - "lodash._baseeach": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash._baseeach/-/lodash._baseeach-3.0.4.tgz", - "integrity": "sha1-z4cGVyyhROjZ11InyZDamC+TKvM=", - "requires": { - "lodash.keys": "^3.0.0" - } - }, - "lodash._basefind": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._basefind/-/lodash._basefind-3.0.0.tgz", - "integrity": "sha1-srugXMZF+XLeLPkl+iv2Og9gyK4=" - }, - "lodash._basefindindex": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/lodash._basefindindex/-/lodash._basefindindex-3.6.0.tgz", - "integrity": "sha1-8IM2ChsCJBjtgbyJm+sxLiHnSk8=" - }, - "lodash._baseisequal": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz", - "integrity": "sha1-2AJfdjOdKTQnZ9zIh85cuVpbUfE=", - "requires": { - "lodash.isarray": "^3.0.0", - "lodash.istypedarray": "^3.0.0", - "lodash.keys": "^3.0.0" - } - }, - "lodash._baseismatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lodash._baseismatch/-/lodash._baseismatch-3.1.3.tgz", - "integrity": "sha1-Byj8SO+hFpnT1fLXMEnyqxPED9U=", - "requires": { - "lodash._baseisequal": "^3.0.0" - } - }, - "lodash._basematches": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash._basematches/-/lodash._basematches-3.2.0.tgz", - "integrity": "sha1-9H4D8H7CB4SrCWjQy2y1l+IQEVg=", - "requires": { - "lodash._baseismatch": "^3.0.0", - "lodash.pairs": "^3.0.0" - } - }, - "lodash._bindcallback": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz", - "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=" - }, - "lodash._getnative": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" - }, - "lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, - "lodash.cond": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz", - "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=", - "dev": true - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" - }, - "lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" - }, - "lodash.every": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.every/-/lodash.every-4.6.0.tgz", - "integrity": "sha1-64mYS+vENkJ5uzrvu9HKGb+mxqc=" - }, - "lodash.find": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", - "integrity": "sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=" - }, - "lodash.findwhere": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.findwhere/-/lodash.findwhere-3.1.0.tgz", - "integrity": "sha1-eTfTTz6sgY3sf6lOjKXib9uhz8E=", - "requires": { - "lodash._basematches": "^3.0.0", - "lodash.find": "^3.0.0" - }, - "dependencies": { - "lodash.find": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-3.2.1.tgz", - "integrity": "sha1-BG4xnzrOkSrGySRsf2g8XsB7Nq0=", - "requires": { - "lodash._basecallback": "^3.0.0", - "lodash._baseeach": "^3.0.0", - "lodash._basefind": "^3.0.0", - "lodash._basefindindex": "^3.0.0", - "lodash.isarray": "^3.0.0", - "lodash.keys": "^3.0.0" - } - } - } - }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, - "lodash.flowright": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/lodash.flowright/-/lodash.flowright-3.5.0.tgz", - "integrity": "sha1-K1//OZcW1+fcVyT+k0n2cGUYTWc=" - }, - "lodash.foreach": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" - }, - "lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" - }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" - }, - "lodash.isarray": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=" - }, - "lodash.isempty": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", - "integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4=" - }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" - }, - "lodash.isobject": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", - "integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.istypedarray": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz", - "integrity": "sha1-yaR3SYYHUB2OhJTSg7h8OSgc72I=" - }, - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" - } - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "lodash.merge": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", - "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==" - }, - "lodash.mergewith": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz", - "integrity": "sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU=" - }, - "lodash.omit": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", - "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=" - }, - "lodash.pairs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash.pairs/-/lodash.pairs-3.0.1.tgz", - "integrity": "sha1-u+CNV4bu6qCaFckevw3LfSvjJqk=", - "requires": { - "lodash.keys": "^3.0.0" - } - }, - "lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, - "lodash.template": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", - "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", - "requires": { - "lodash._reinterpolate": "~3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "lodash.templatesettings": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", - "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", - "requires": { - "lodash._reinterpolate": "~3.0.0" - } - }, - "lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=" - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" - }, - "lodash.without": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.without/-/lodash.without-4.4.0.tgz", - "integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=" - }, - "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" - }, - "loose-envify": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", - "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", - "requires": { - "js-tokens": "^3.0.0" - } - }, - "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", - "requires": { - "es5-ext": "~0.10.2" - } - }, - "mailchimp": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mailchimp/-/mailchimp-1.2.0.tgz", - "integrity": "sha1-y6v5khIOX3IlypSEpDk4RKWCcrQ=", - "requires": { - "qs": "^6.2.0", - "request": "^2.72.0" - } - }, - "make-error": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.2.tgz", - "integrity": "sha512-l9ra35l5VWLF24y75Tg8XgfGLX0ueRhph118WKM6H5denx4bB5QF59+4UAm9oJ2qsPQZas/CQUDdtDdfvYHBdQ==" - }, - "make-error-cause": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/make-error-cause/-/make-error-cause-1.2.2.tgz", - "integrity": "sha1-3wOI/NCzeBbf8KX7gQiTl3fcvJ0=", - "requires": { - "make-error": "^1.2.0" - } - }, - "markdown-escapes": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.1.tgz", - "integrity": "sha1-GZTfLTr0gR3lmmcUk0wrIpJzRRg=" - }, - "marked": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.9.tgz", - "integrity": "sha512-nW5u0dxpXxHfkHzzrveY45gCbi+R4PaO4WRZYqZNl+vB0hVGeqlFn0aOg1c8AKL63TrNFn9Bm2UP4AdiZ9TPLw==" - }, - "math-random": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", - "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", - "dev": true - }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "memoizee": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.3.10.tgz", - "integrity": "sha1-TsoNiu057J0Bf0xcLy9kMvQuXI8=", - "requires": { - "d": "~0.1.1", - "es5-ext": "~0.10.11", - "es6-weak-map": "~0.1.4", - "event-emitter": "~0.3.4", - "lru-queue": "0.1", - "next-tick": "~0.2.2", - "timers-ext": "0.1" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "message-box": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/message-box/-/message-box-0.2.0.tgz", - "integrity": "sha512-SPLfVDEM2YcAgV2IB0B5vOGjvqXSSw7ZibEeXcff8HYpxyG1Uj+XjgnGUGyR1C0EQCvPI3MBx3p7opt2CIQ2hw==", - "requires": { - "lodash.merge": "^4.6.0", - "lodash.template": "^4.4.0" - } - }, - "metascraper": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/metascraper/-/metascraper-1.0.7.tgz", - "integrity": "sha512-UPGR9eGM4u6iS3oWNS/KQoHrfINz8N33a1astWECiLz9st6e1PUc7+sGyHvQuzXDzYvc+Qu0/TrzOWUNuw+Fcg==", - "requires": { - "cheerio": "^0.20.0", - "chrono-node": "^1.2.3", - "is-isodate": "0.0.1", - "is-url": "^1.2.1", - "popsicle": "^6.2.0", - "to-title-case": "^1.0.0" - }, - "dependencies": { - "acorn": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz", - "integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc=" - }, - "acorn-globals": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.9.tgz", - "integrity": "sha1-VbtemGkVB7dFedBRNBMhfDgMVM8=", - "optional": true, - "requires": { - "acorn": "^2.1.0" - } - }, - "cheerio": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.20.0.tgz", - "integrity": "sha1-XHEPK6uVZTJyhCugHG6mGzVF7DU=", - "requires": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "~3.8.1", - "jsdom": "^7.0.2", - "lodash": "^4.1.0" - }, - "dependencies": { - "jsdom": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-7.2.2.tgz", - "integrity": "sha1-QLQCdwwr2iNGkJa+6Rq2deOx/G4=", - "optional": true, - "requires": { - "abab": "^1.0.0", - "acorn": "^2.4.0", - "acorn-globals": "^1.0.4", - "cssom": ">= 0.3.0 < 0.4.0", - "cssstyle": ">= 0.2.29 < 0.3.0", - "escodegen": "^1.6.1", - "nwmatcher": ">= 1.3.7 < 2.0.0", - "parse5": "^1.5.1", - "request": "^2.55.0", - "sax": "^1.1.4", - "symbol-tree": ">= 3.1.0 < 4.0.0", - "tough-cookie": "^2.2.0", - "webidl-conversions": "^2.0.0", - "whatwg-url-compat": "~0.6.5", - "xml-name-validator": ">= 2.0.1 < 3.0.0" - } - } - } - }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "css-what": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", - "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=" - }, - "cssstyle": { - "version": "0.2.37", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz", - "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=", - "optional": true, - "requires": { - "cssom": "0.3.x" - } - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "parse5": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz", - "integrity": "sha1-m387DeMr543CQBsXVzzK8Pb1nZQ=", - "optional": true - }, - "webidl-conversions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-2.0.1.tgz", - "integrity": "sha1-O/glj30xjHRDw28uFpQCoaZwNQY=", - "optional": true - }, - "xml-name-validator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz", - "integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=", - "optional": true - } - } - }, - "meteor-node-stubs": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/meteor-node-stubs/-/meteor-node-stubs-0.2.11.tgz", - "integrity": "sha1-cV5Owc6IgkiylgThbQkiVrDLfjQ=", - "requires": { - "assert": "^1.4.1", - "browserify-zlib": "^0.1.4", - "buffer": "^4.9.1", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.7", - "events": "^1.1.1", - "http-browserify": "^1.7.0", - "https-browserify": "0.0.1", - "os-browserify": "^0.2.1", - "path-browserify": "0.0.0", - "process": "^0.11.9", - "punycode": "^1.4.1", - "querystring-es3": "^0.2.1", - "readable-stream": "git+https://github.com/meteor/readable-stream.git#c688cdd193549919b840e8d72a86682d91961e12", - "stream-browserify": "^2.0.1", - "string_decoder": "^1.0.1", - "timers-browserify": "^1.4.2", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.10.3", - "vm-browserify": "0.0.4" - }, - "dependencies": { - "Base64": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz", - "integrity": "sha1-ujpCMHCOGGcFBl5mur3Uw1z2ACg=" - }, - "asn1.js": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.0.tgz", - "integrity": "sha1-9xoSQ/PnnUbXsH1/v0gk7nOvBUo=", - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "requires": { - "util": "0.10.3" - } - }, - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=" - }, - "base64-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz", - "integrity": "sha1-o5mS1yNYSBGYK+XikLtqU9hnAPE=" - }, - "bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" - }, - "brace-expansion": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", - "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", - "requires": { - "balanced-match": "^0.4.1", - "concat-map": "0.0.1" - } - }, - "brorand": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.6.tgz", - "integrity": "sha1-QChwa5FfkfezSaLgvzw3YDnSFuU=" - }, - "browserify-aes": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.6.tgz", - "integrity": "sha1-Xncl297x/Vkw1OurSFZ85FHEigo=", - "requires": { - "buffer-xor": "^1.0.2", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "inherits": "^2.0.1" - } - }, - "browserify-cipher": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", - "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", - "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.0.tgz", - "integrity": "sha1-EHc5EMPCBtVCCkaq2GlPgguFlo8=", - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" - } - }, - "browserify-zlib": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", - "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", - "requires": { - "pako": "~0.2.0" - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" - }, - "cipher-base": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.3.tgz", - "integrity": "sha1-7qvxlEGc6QDaMBjCB9IS8qbfCgc=", - "requires": { - "inherits": "^2.0.1" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "requires": { - "date-now": "^0.1.4" - } - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" - }, - "create-ecdh": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", - "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - } - }, - "create-hash": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.2.tgz", - "integrity": "sha1-USEAYte7dHn2xlu0GpIgix1hq60=", - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "ripemd160": "^1.0.0", - "sha.js": "^2.3.6" - } - }, - "create-hmac": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.4.tgz", - "integrity": "sha1-0/tLolPriz9W456i+8uK90e9MXA=", - "requires": { - "create-hash": "^1.1.0", - "inherits": "^2.0.1" - } - }, - "crypto-browserify": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.0.tgz", - "integrity": "sha1-NlKgkGq5sqfgw85mpAjpV6JIVSI=", - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "diffie-hellman": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", - "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "domain-browser": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", - "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=" - }, - "elliptic": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.2.tgz", - "integrity": "sha1-5MgeCCnPCmWrcOmYuCMnI7XBvEg=", - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "inherits": "^2.0.1" - } - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" - }, - "evp_bytestokey": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz", - "integrity": "sha1-SXtmrZ/vZc18CKYYCCS6FHa2blM=", - "requires": { - "create-hash": "^1.1.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "hash.js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz", - "integrity": "sha1-EzL/ABVsCg/92CNgE9B7d6BFFXM=", - "requires": { - "inherits": "^2.0.1" - } - }, - "http-browserify": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/http-browserify/-/http-browserify-1.7.0.tgz", - "integrity": "sha1-M3la3nLfiKz7/TZ3PO/tp2RzWyA=", - "requires": { - "Base64": "~0.2.0", - "inherits": "~2.0.1" - } - }, - "https-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", - "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=" - }, - "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "miller-rabin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.0.tgz", - "integrity": "sha1-SmL7HUKTPAVYOYL0xxb2+55sbT0=", - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - } - }, - "minimalistic-assert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", - "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "os-browserify": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", - "integrity": "sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8=" - }, - "pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" - }, - "parse-asn1": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.0.0.tgz", - "integrity": "sha1-NQYPbVAV03Yox3D04JGgtaJ4vCM=", - "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3" - } - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "pbkdf2": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.9.tgz", - "integrity": "sha1-8sSyWmAAWLPDdzwIbDfbvuH/5pM=", - "requires": { - "create-hmac": "^1.1.2" - } - }, - "process": { - "version": "0.11.9", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.9.tgz", - "integrity": "sha1-e9WtIapiU+fahoImTx4R0RwDGME=" - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" - }, - "public-encrypt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", - "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1" - } - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" - }, - "randombytes": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.3.tgz", - "integrity": "sha1-Z0yZdgkBw8QRJ3GjHlIdw0nMCew=" - }, - "readable-stream": { - "version": "git+https://github.com/meteor/readable-stream.git#c688cdd193549919b840e8d72a86682d91961e12", - "from": "git+https://github.com/meteor/readable-stream.git", - "requires": { - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "safe-buffer": "^5.0.1", - "string_decoder": "~1.0.0", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", - "requires": { - "glob": "^7.0.5" - } - }, - "ripemd160": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-1.0.1.tgz", - "integrity": "sha1-k6S71JQrxXS2mo+lfHHeEOzKfW4=" - }, - "safe-buffer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", - "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=" - }, - "sha.js": { - "version": "2.4.8", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.8.tgz", - "integrity": "sha1-NwaMLEdra69ALRSknGf1l5IfY08=", - "requires": { - "inherits": "^2.0.1" - } - }, - "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "string_decoder": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz", - "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "timers-browserify": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", - "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", - "requires": { - "process": "~0.11.0" - } - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" - } - } - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "requires": { - "inherits": "2.0.1" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "requires": { - "indexof": "0.0.1" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - } - } - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" - }, - "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "requires": { - "mime-db": "~1.30.0" - } - }, - "mimer": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/mimer/-/mimer-0.2.3.tgz", - "integrity": "sha512-cICHJPMZUdZMqWaOQ+Eh0hHo1R6IUCiBee7WvIGGUJsZyjdMUInxQVmyu8hKj5uCy+Bi+Wlp/EsdUR61yOdWOw==" - }, - "mingo": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/mingo/-/mingo-2.2.2.tgz", - "integrity": "sha1-vmnUhq5uCsVLl53F9EEtshhR9pM=" - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } - } - }, - "moment": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz", - "integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg==" - }, - "mongo-object": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/mongo-object/-/mongo-object-0.1.2.tgz", - "integrity": "sha512-xRrnal5HuCz3we8Bzk17iYfgCMfaaSU+cq0OkQ/PP+CYhhFmw4Joqmcc0R9XUAgxbFAybg7uzxbNGUw13kEUxQ==", - "requires": { - "lodash.foreach": "^4.5.0", - "lodash.isempty": "^4.4.0", - "lodash.isobject": "^3.0.2", - "lodash.without": "^4.4.0" - } - }, - "moo": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/moo/-/moo-0.4.3.tgz", - "integrity": "sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw==", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "mute-stream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", - "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", - "dev": true - }, - "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "nearley": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.15.0.tgz", - "integrity": "sha512-ZjzdO+yBtMrRrBbr+BJ35ECla6PGCAb/6hqpBQe7bmhEJabQ4rpVdj4sadP1Z1jQGyaDmm1GciQWsGVxIZ3uJA==", - "dev": true, - "requires": { - "moo": "^0.4.3", - "nomnom": "~1.6.2", - "railroad-diagrams": "^1.0.0", - "randexp": "0.4.6", - "semver": "^5.4.1" - } - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" - }, - "next-tick": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-0.2.2.tgz", - "integrity": "sha1-ddpKkn7liH45BliABltzNkE7MQ0=" - }, - "node-fetch": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", - "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", - "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } - }, - "nomnom": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz", - "integrity": "sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE=", - "dev": true, - "requires": { - "colors": "0.5.x", - "underscore": "~1.4.4" - }, - "dependencies": { - "underscore": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", - "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=", - "dev": true - } - } - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-scroll-left": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-scroll-left/-/normalize-scroll-left-0.1.2.tgz", - "integrity": "sha512-F9YMRls0zCF6BFIE2YnXDRpHPpfd91nOIaNdDgrx5YMoPLo8Wqj+6jNXHQsYBavJeXP4ww8HCt0xQAKc5qk2Fg==" - }, - "nth-check": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", - "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", - "requires": { - "boolbase": "~1.0.0" - } - }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "nwmatcher": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.4.tgz", - "integrity": "sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ==", - "optional": true - }, - "nwsapi": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.7.tgz", - "integrity": "sha512-VZXniaaaORAXGCNsvUNefsKRQYk8zCzQZ57jalgrpHcU70OrAzKAiN/3plYtH/VPRmZeYyUzQiYfKzcMXC1g5Q==", - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-inspect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", - "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", - "dev": true - }, - "object-is": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", - "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=", - "dev": true - }, - "object-keys": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", - "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" - }, - "object-path": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.4.tgz", - "integrity": "sha1-NwrnUvvzfePqcKhhwju6iRVpGUk=" - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.entries": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.0.4.tgz", - "integrity": "sha1-G/mk3SKI9bM/Opk9JXZh8F0WGl8=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.6.1", - "function-bind": "^1.1.0", - "has": "^1.0.1" - } - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" - } - }, - "object.values": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.0.4.tgz", - "integrity": "sha1-5STaCbT2b/Bd9FdUbscqyZ8TBpo=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.6.1", - "function-bind": "^1.1.0", - "has": "^1.0.1" - } - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", - "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - }, - "dependencies": { - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" - } - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-limit": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", - "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", - "dev": true - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", - "dev": true - }, - "parse-entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.1.1.tgz", - "integrity": "sha1-gRLYhHExnyerrk1klksSL+ThuJA=", - "requires": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" - } - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true - }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "pkg-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", - "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", - "dev": true, - "requires": { - "find-up": "^1.0.0" - } - }, - "pluralize": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", - "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", - "dev": true - }, - "pn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", - "dev": true - }, - "popper.js": { - "version": "1.14.4", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.4.tgz", - "integrity": "sha1-juwdj/AqWjoVLdQ0FKFce3n9abY=" - }, - "popsicle": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/popsicle/-/popsicle-6.2.2.tgz", - "integrity": "sha1-4nPIvUgYH3OlmxmeKl4ltpKz4jc=", - "requires": { - "any-promise": "^1.3.0", - "arrify": "^1.0.0", - "concat-stream": "^1.4.7", - "form-data": "^0.2.0", - "make-error-cause": "^1.0.1", - "throwback": "^1.1.0", - "tough-cookie": "^2.0.0", - "xtend": "^4.0.0" - }, - "dependencies": { - "async": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" - }, - "combined-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", - "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=", - "requires": { - "delayed-stream": "0.0.5" - } - }, - "delayed-stream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", - "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8=" - }, - "form-data": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.2.0.tgz", - "integrity": "sha1-Jvi8JtpkQOKZy9z7aQNcT3em5GY=", - "requires": { - "async": "~0.9.0", - "combined-stream": "~0.0.4", - "mime-types": "~2.0.3" - } - }, - "mime-db": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz", - "integrity": "sha1-PQxjGA9FjrENMlqqN9fFiuMS6dc=" - }, - "mime-types": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz", - "integrity": "sha1-MQ4VnbI+B3+Lsit0jav6SVcUCqY=", - "requires": { - "mime-db": "~1.12.0" - } - } - } - }, - "postcss": { - "version": "6.0.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.15.tgz", - "integrity": "sha512-v/SpyMzLbtkmh45zUdaqLAaqXqzPdSrw8p4cQVO0/w6YiYfpj4k+Wkzhn68qk9br+H+0qfddhdPEVnbmBPfXVQ==", - "requires": { - "chalk": "^2.3.0", - "source-map": "^0.6.1", - "supports-color": "^5.1.0" - }, - "dependencies": { - "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", - "requires": { - "ansi-styles": "^3.1.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^4.0.0" - }, - "dependencies": { - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "requires": { - "has-flag": "^2.0.0" - } - } - } - }, - "supports-color": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz", - "integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==", - "requires": { - "has-flag": "^2.0.0" - } - } - } - }, - "postcss-value-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", - "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=" - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, - "pretty-format": { - "version": "23.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.2.0.tgz", - "integrity": "sha1-OwqqY8AYpTWDNzwcs6XZbMXoMBc=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0", - "ansi-styles": "^3.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - } - } - }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" - }, - "progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", - "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", - "dev": true - }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "requires": { - "asap": "~2.0.3" - } - }, - "prop-types": { - "version": "15.6.0", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.0.tgz", - "integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=", - "requires": { - "fbjs": "^0.8.16", - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" - } - }, - "prop-types-extra": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.0.1.tgz", - "integrity": "sha1-pXvUgQ6C0no/9DF+zBtK0AX3moI=", - "requires": { - "warning": "^3.0.0" - } - }, - "protobufjs": { - "version": "6.8.8", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", - "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", - "requires": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.0", - "@types/node": "^10.1.0", - "long": "^4.0.0" - }, - "dependencies": { - "@types/node": { - "version": "10.11.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.11.3.tgz", - "integrity": "sha512-3AvcEJAh9EMatxs+OxAlvAEs7OTy6AG94mcH1iqyVDwVVndekLxzwkWQ/Z4SDbY6GO2oyUXyWW8tQ4rENSSQVQ==" - } - } - }, - "proxy-addr": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", - "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.5.2" - } - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" - }, - "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "requires": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - } - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" - }, - "raf": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.0.tgz", - "integrity": "sha512-pDP/NMRAXoTfrhCfyfSEwJAKLaxBU9eApMeBPB1TkDouZmvPerIClV8lTAd+uF8ZiTaVl69e1FCxQrAd/VTjGw==", - "dev": true, - "requires": { - "performance-now": "^2.1.0" - } - }, - "railroad-diagrams": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", - "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=", - "dev": true - }, - "ramda": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.25.0.tgz", - "integrity": "sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ==", - "dev": true - }, - "randexp": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", - "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", - "dev": true, - "requires": { - "discontinuous-range": "1.0.0", - "ret": "~0.1.10" - } - }, - "randomatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz", - "integrity": "sha512-VdxFOIEY3mNO5PtSRkkle/hPJDHvQhK21oa73K4yAc9qmp6N429gAyF1gZMOTMeS0/AYzaV/2Trcef+NaIonSA==", - "dev": true, - "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" - }, - "raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "unpipe": "1.0.0" - } - }, - "react": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.2.0.tgz", - "integrity": "sha512-ZmIomM7EE1DvPEnSFAHZn9Vs9zJl5A9H7el0EGTE6ZbW9FKe/14IYAlPbC8iH25YarEQxZL+E8VW7Mi7kfQrDQ==", - "requires": { - "fbjs": "^0.8.16", - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.0" - } - }, - "react-addons-pure-render-mixin": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/react-addons-pure-render-mixin/-/react-addons-pure-render-mixin-15.6.2.tgz", - "integrity": "sha1-a4P0C2s27kBzXL1hJes/E84c3ck=", - "requires": { - "fbjs": "^0.8.4", - "object-assign": "^4.1.0" - } - }, - "react-apollo": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/react-apollo/-/react-apollo-2.2.4.tgz", - "integrity": "sha512-haS5R30Qvteb65ZLfWomUZQh47VU4ld4Kof3zlqdbLOrYPt3/DdVZC8ZFPZSxd5zPeIJtZqpUfAxD1WHVoMPIA==", - "requires": { - "fbjs": "^1.0.0", - "hoist-non-react-statics": "^3.0.0", - "invariant": "^2.2.2", - "lodash.flowright": "^3.5.0", - "lodash.isequal": "^4.5.0", - "prop-types": "^15.6.0" - }, - "dependencies": { - "fbjs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-1.0.0.tgz", - "integrity": "sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA==", - "requires": { - "core-js": "^2.4.1", - "fbjs-css-vars": "^1.0.0", - "isomorphic-fetch": "^2.1.1", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.18" - } - }, - "hoist-non-react-statics": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.0.1.tgz", - "integrity": "sha512-1kXwPsOi0OGQIZNVMPvgWJ9tSnGMiMfJdihqEzrPEXlHOBh9AAHXX/QYmAJTXztnz/K+PQ8ryCb4eGaN6HlGbQ==", - "requires": { - "react-is": "^16.3.2" - } - }, - "ua-parser-js": { - "version": "0.7.18", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz", - "integrity": "sha512-LtzwHlVHwFGTptfNSgezHp7WUlwiqb0gA9AALRbKaERfxwJoiX0A73QbTToxteIAuIaFshhgIZfqK8s7clqgnA==" - } - } - }, - "react-bootstrap": { - "version": "0.32.1", - "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-0.32.1.tgz", - "integrity": "sha512-RbfzKUbsukWsToWqGHfCCyMFq9QQI0TznutdyxyJw6dih2NvIne25Mrssg8LZsprqtPpyQi8bN0L0Fx3fUsL8Q==", - "requires": { - "babel-runtime": "^6.11.6", - "classnames": "^2.2.5", - "dom-helpers": "^3.2.0", - "invariant": "^2.2.1", - "keycode": "^2.1.2", - "prop-types": "^15.5.10", - "prop-types-extra": "^1.0.1", - "react-overlays": "^0.8.0", - "react-prop-types": "^0.4.0", - "react-transition-group": "^2.0.0", - "uncontrollable": "^4.1.0", - "warning": "^3.0.0" - }, - "dependencies": { - "react-overlays": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.8.3.tgz", - "integrity": "sha512-h6GT3jgy90PgctleP39Yu3eK1v9vaJAW73GOA/UbN9dJ7aAN4BTZD6793eI1D5U+ukMk17qiqN/wl3diK1Z5LA==", - "requires": { - "classnames": "^2.2.5", - "dom-helpers": "^3.2.1", - "prop-types": "^15.5.10", - "prop-types-extra": "^1.0.1", - "react-transition-group": "^2.2.0", - "warning": "^3.0.0" - } - } - } - }, - "react-bootstrap-datetimepicker": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/react-bootstrap-datetimepicker/-/react-bootstrap-datetimepicker-0.0.22.tgz", - "integrity": "sha1-B+RI2ZMVfQSa0IdtD5o8nFAp2cU=", - "requires": { - "babel-runtime": "^5.6.18", - "classnames": "^2.1.2", - "moment": "^2.8.2" - }, - "dependencies": { - "babel-runtime": { - "version": "5.8.38", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.38.tgz", - "integrity": "sha1-HAsC62MxL18If/IEUIJ7QlydTBk=", - "requires": { - "core-js": "^1.0.0" - } - }, - "core-js": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", - "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" - } - } - }, - "react-cookie": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-2.1.6.tgz", - "integrity": "sha512-Cu2U2JgrVdc/ExdRGAxo0iy4tpl8QpcC37Z1l+AYNsZaA+UP9VQ2buP+meKF8P7AXv0yfG+ac81KlGwdOWr3YQ==", - "requires": { - "hoist-non-react-statics": "^2.3.1", - "prop-types": "^15.0.0", - "universal-cookie": "^2.1.5" - } - }, - "react-datetime": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/react-datetime/-/react-datetime-2.11.1.tgz", - "integrity": "sha512-yDZdygP/2zeq02yYnRxid8rDnlUXwCA+0WeHOScc8wZqBnxfkrDEhYJSo/lKaX+85dIU7uuBCuG5oTkl4xXAYQ==", - "requires": { - "create-react-class": "^15.5.2", - "object-assign": "^3.0.0", - "prop-types": "^15.5.7", - "react-onclickoutside": "^6.5.0" - }, - "dependencies": { - "object-assign": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", - "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=" - } - } - }, - "react-dom": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.2.0.tgz", - "integrity": "sha512-zpGAdwHVn9K0091d+hr+R0qrjoJ84cIBFL2uU60KvWBPfZ7LPSrfqviTxGHWN0sjPZb2hxWzMexwrvJdKePvjg==", - "requires": { - "fbjs": "^0.8.16", - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.0" - } - }, - "react-dropzone": { - "version": "3.13.4", - "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-3.13.4.tgz", - "integrity": "sha1-hNomgVxAM5aRxJtFRMLvehaRLMw=", - "requires": { - "attr-accept": "^1.0.3", - "prop-types": "^15.5.7" - } - }, - "react-event-listener": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.6.4.tgz", - "integrity": "sha512-t7VSjIuUFmN+GeyKb+wm025YLeojVB85kJL6sSs0wEBJddfmKBEQz+CNBZ2zBLKVWkPy/fZXM6U5yvojjYBVYQ==", - "requires": { - "@babel/runtime": "7.0.0", - "prop-types": "^15.6.0", - "warning": "^4.0.1" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz", - "integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==", - "requires": { - "regenerator-runtime": "^0.12.0" - } - }, - "regenerator-runtime": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", - "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" - }, - "warning": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.2.tgz", - "integrity": "sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==", - "requires": { - "loose-envify": "^1.0.0" - } - } - } - }, - "react-helmet": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-5.2.0.tgz", - "integrity": "sha1-qBgR3yExOm1VxfBYxK66XW89l6c=", - "requires": { - "deep-equal": "^1.0.1", - "object-assign": "^4.1.1", - "prop-types": "^15.5.4", - "react-side-effect": "^1.1.0" - } - }, - "react-intl": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.4.0.tgz", - "integrity": "sha1-ZsFNyd+ac7L7v71gIXJugKYT6xU=", - "requires": { - "intl-format-cache": "^2.0.5", - "intl-messageformat": "^2.1.0", - "intl-relativeformat": "^2.0.0", - "invariant": "^2.1.1" - } - }, - "react-is": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.4.1.tgz", - "integrity": "sha512-xpb0PpALlFWNw/q13A+1aHeyJyLYCg0/cCHPUA43zYluZuIPHaHL3k8OBsTgQtxqW0FhyDEMvi8fZ/+7+r4OSQ==" - }, - "react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" - }, - "react-loadable": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/react-loadable/-/react-loadable-4.0.5.tgz", - "integrity": "sha1-Il66QKDmfMVpi8tdtueD8NOp5yk=", - "requires": { - "import-inspector": "^2.0.0", - "is-webpack-bundle": "^1.0.0", - "webpack-require-weak": "^1.0.1" - } - }, - "react-markdown": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-3.1.4.tgz", - "integrity": "sha512-i8WueytRXbYzyJ2GemIOTMRx/NigPo8r4m3R/KvWD7r+PxPyc9ke66cI3DR7MBRSS+nVG82VWEgRDE1VaZUCqA==", - "requires": { - "prop-types": "^15.6.0", - "remark-parse": "^4.0.0", - "unified": "^6.1.5", - "unist-util-visit": "^1.1.3", - "xtend": "^4.0.1" - } - }, - "react-onclickoutside": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.7.0.tgz", - "integrity": "sha512-IBivBP7xayM7SbbVlAnKgHgoWdfCVqnNBNgQRY5x9iFQm55tFdolR02hX1fCJJtTEKnbaL1stB72/TZc6+p2+Q==" - }, - "react-places-autocomplete": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/react-places-autocomplete/-/react-places-autocomplete-5.4.3.tgz", - "integrity": "sha512-NXTYVLyYESsW5LgYlOP3+7Bs+mWeXK8KAqrht2X48+cPYFHmT/S8BNnIRUxpmZyS036s1wuItuwpCJ53nYR/zQ==", - "requires": { - "lodash.debounce": "^4.0.8", - "prop-types": "^15.5.8" - } - }, - "react-prop-types": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/react-prop-types/-/react-prop-types-0.4.0.tgz", - "integrity": "sha1-+ZsL+0AGkpya8gUefBQUpcdbk9A=", - "requires": { - "warning": "^3.0.0" - } - }, - "react-reconciler": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.7.0.tgz", - "integrity": "sha512-50JwZ3yNyMS8fchN+jjWEJOH3Oze7UmhxeoJLn2j6f3NjpfCRbcmih83XTWmzqtar/ivd5f7tvQhvvhism2fgg==", - "dev": true, - "requires": { - "fbjs": "^0.8.16", - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.0" - } - }, - "react-redux": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.6.tgz", - "integrity": "sha512-8taaaGu+J7PMJQDJrk/xiWEYQmdo3mkXw6wPr3K3LxvXis3Fymiq7c13S+Tpls/AyNUAsoONkU81AP0RA6y6Vw==", - "requires": { - "hoist-non-react-statics": "^2.2.1", - "invariant": "^2.0.0", - "lodash": "^4.2.0", - "lodash-es": "^4.2.0", - "loose-envify": "^1.1.0", - "prop-types": "^15.5.10" - } - }, - "react-router": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-3.2.0.tgz", - "integrity": "sha512-sXlLOg0TRCqnjCVskqBHGjzNjcJKUqXEKnDSuxMYJSPJNq9hROE9VsiIW2kfIq7Ev+20Iz0nxayekXyv0XNmsg==", - "requires": { - "create-react-class": "^15.5.1", - "history": "^3.0.0", - "hoist-non-react-statics": "^1.2.0", - "invariant": "^2.2.1", - "loose-envify": "^1.2.0", - "prop-types": "^15.5.6", - "warning": "^3.0.0" - }, - "dependencies": { - "hoist-non-react-statics": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", - "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs=" - } - } - }, - "react-router-bootstrap": { - "version": "0.24.4", - "resolved": "https://registry.npmjs.org/react-router-bootstrap/-/react-router-bootstrap-0.24.4.tgz", - "integrity": "sha512-kEwk3ml4wvE3IbJvRVjx0zBBBxW4JLhD0wyy0hBdlWSdfjvgoHVvlxx9gBPxvEs5VwWlbFvNRyUghLZ2AMcmzg==", - "requires": { - "prop-types": "^15.5.10" - } - }, - "react-router-dom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.3.1.tgz", - "integrity": "sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==", - "requires": { - "history": "^4.7.2", - "invariant": "^2.2.4", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.1", - "react-router": "^4.3.1", - "warning": "^4.0.1" - }, - "dependencies": { - "history": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz", - "integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==", - "requires": { - "invariant": "^2.2.1", - "loose-envify": "^1.2.0", - "resolve-pathname": "^2.2.0", - "value-equal": "^0.4.0", - "warning": "^3.0.0" - }, - "dependencies": { - "warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", - "requires": { - "loose-envify": "^1.0.0" - } - } - } - }, - "hoist-non-react-statics": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", - "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "requires": { - "loose-envify": "^1.0.0" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", - "requires": { - "isarray": "0.0.1" - } - }, - "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", - "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" - } - }, - "react-router": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz", - "integrity": "sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==", - "requires": { - "history": "^4.7.2", - "hoist-non-react-statics": "^2.5.0", - "invariant": "^2.2.4", - "loose-envify": "^1.3.1", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.1", - "warning": "^4.0.1" - } - }, - "warning": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.2.tgz", - "integrity": "sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==", - "requires": { - "loose-envify": "^1.0.0" - } - } - } - }, - "react-router-scroll": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/react-router-scroll/-/react-router-scroll-0.4.4.tgz", - "integrity": "sha512-FR+kyNmRrNqhRbMHDFSgFPVrOy923AdJZE0Qqefub5u56+5d7EENLy4DOrCZVr2fTHsJjWJDE0X1vU063t4bOA==", - "requires": { - "prop-types": "^15.6.0", - "scroll-behavior": "^0.9.5", - "warning": "^3.0.0" - } - }, - "react-side-effect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-1.1.3.tgz", - "integrity": "sha1-USwlq+DewXKDTEAB7FxR4E1BvFw=", - "requires": { - "exenv": "^1.2.1", - "shallowequal": "^1.0.1" - } - }, - "react-stripe-checkout": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/react-stripe-checkout/-/react-stripe-checkout-2.6.3.tgz", - "integrity": "sha1-MXOocLBOWjwyGgbSTNU8YDARHEU=" - }, - "react-test-renderer": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.4.1.tgz", - "integrity": "sha512-wyyiPxRZOTpKnNIgUBOB6xPLTpIzwcQMIURhZvzUqZzezvHjaGNsDPBhMac5fIY3Jf5NuKxoGvV64zDSOECPPQ==", - "dev": true, - "requires": { - "fbjs": "^0.8.16", - "object-assign": "^4.1.1", - "prop-types": "^15.6.0", - "react-is": "^16.4.1" - } - }, - "react-transition-group": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.2.1.tgz", - "integrity": "sha512-q54UBM22bs/CekG8r3+vi9TugSqh0t7qcEVycaRc9M0p0aCEu+h6rp/RFiW7fHfgd1IKpd9oILFTl5QK+FpiPA==", - "requires": { - "chain-function": "^1.0.0", - "classnames": "^2.2.5", - "dom-helpers": "^3.2.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.5.8", - "warning": "^3.0.0" - } - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - } - } - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", - "util-deprecate": "~1.0.1" - } - }, - "readline2": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", - "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "mute-stream": "0.0.5" - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "^1.1.6" - } - }, - "recompose": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.26.0.tgz", - "integrity": "sha512-KwOu6ztO0mN5vy3+zDcc45lgnaUoaQse/a5yLVqtzTK13czSWnFGmXbQVmnoMgDkI5POd1EwIKSbjU1V7xdZog==", - "requires": { - "change-emitter": "^0.1.2", - "fbjs": "^0.8.1", - "hoist-non-react-statics": "^2.3.1", - "symbol-observable": "^1.0.4" - } - }, - "redux": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz", - "integrity": "sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==", - "requires": { - "lodash": "^4.2.1", - "lodash-es": "^4.2.1", - "loose-envify": "^1.1.0", - "symbol-observable": "^1.0.3" - } - }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "requires": { - "is-equal-shallow": "^0.1.3" - } - }, - "remark-parse": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-4.0.0.tgz", - "integrity": "sha512-XZgICP2gJ1MHU7+vQaRM+VA9HEL3X253uwUM/BGgx3iv6TH2B3bF3B8q00DKcyP9YrJV+/7WOWEWBFF/u8cIsw==", - "requires": { - "collapse-white-space": "^1.0.2", - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-whitespace-character": "^1.0.0", - "is-word-character": "^1.0.0", - "markdown-escapes": "^1.0.0", - "parse-entities": "^1.0.2", - "repeat-string": "^1.5.4", - "state-toggle": "^1.0.0", - "trim": "0.0.1", - "trim-trailing-lines": "^1.0.0", - "unherit": "^1.0.4", - "unist-util-remove-position": "^1.0.0", - "vfile-location": "^2.0.0", - "xtend": "^4.0.1" - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "remove-trailing-slash": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-slash/-/remove-trailing-slash-0.1.0.tgz", - "integrity": "sha1-FJjl3wmEwn5Jt26/Boh8otARUNI=" - }, - "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "^1.0.0" - } - }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" - }, - "request": { - "version": "2.83.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", - "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", - "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "hawk": "~6.0.2", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", - "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "stringstream": "~0.0.5", - "tough-cookie": "~2.3.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" - } - }, - "request-promise-core": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", - "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", - "dev": true, - "requires": { - "lodash": "^4.13.1" - } - }, - "request-promise-native": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", - "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", - "dev": true, - "requires": { - "request-promise-core": "1.1.1", - "stealthy-require": "^1.1.0", - "tough-cookie": ">=2.3.3" - } - }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" - } - }, - "reselect": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.0.0.tgz", - "integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==" - }, - "resolve": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", - "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", - "dev": true, - "requires": { - "path-parse": "^1.0.5" - } - }, - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true - }, - "resolve-pathname": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", - "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==" - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "dev": true, - "requires": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" - }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "requires": { - "align-text": "^0.1.1" - } - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, - "requires": { - "glob": "^7.0.5" - } - }, - "rss": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/rss/-/rss-1.2.2.tgz", - "integrity": "sha1-UKFpiHYTgTOnT5oF0r3I240nqSE=", - "requires": { - "mime-types": "2.1.13", - "xml": "1.0.1" - }, - "dependencies": { - "mime-db": { - "version": "1.25.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.25.0.tgz", - "integrity": "sha1-wY29fHOl2/b0SgJNwNFloeexw5I=" - }, - "mime-types": { - "version": "2.1.13", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.13.tgz", - "integrity": "sha1-4HqqnGxrmnyjASxpADrSWjnpKog=", - "requires": { - "mime-db": "~1.25.0" - } - } - } - }, - "rst-selector-parser": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz", - "integrity": "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=", - "dev": true, - "requires": { - "lodash.flattendeep": "^4.4.0", - "nearley": "^2.7.10" - } - }, - "run-async": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", - "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", - "dev": true, - "requires": { - "once": "^1.3.0" - } - }, - "rx-lite": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", - "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", - "dev": true - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sanitize-html": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.16.3.tgz", - "integrity": "sha512-XpAJGnkMfNM7AzXLRw225blBB/pE4dM4jzRn98g4r88cfxwN6g+5IsRmCAh/gbhYGm6u6i97zsatMOM7Lr8wyw==", - "requires": { - "htmlparser2": "^3.9.0", - "lodash.clonedeep": "^4.5.0", - "lodash.escaperegexp": "^4.1.2", - "lodash.mergewith": "^4.6.0", - "postcss": "^6.0.14", - "srcset": "^1.0.0", - "xtend": "^4.0.0" - }, - "dependencies": { - "domutils": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.6.2.tgz", - "integrity": "sha1-GVjMC0yUJuntNn+xyOhUiRsPo/8=", - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "htmlparser2": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", - "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", - "requires": { - "domelementtype": "^1.3.0", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^2.0.2" - } - } - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "scroll-behavior": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/scroll-behavior/-/scroll-behavior-0.9.5.tgz", - "integrity": "sha512-/5CtMX6YHmCrcV6AICYqFpNqYgx5v6YOyDTeMgVFdLZpgU+T3JXmgV+9s4R+uApcyYwcc7o8Nwp7VTt/ue8y0Q==", - "requires": { - "dom-helpers": "^3.2.1", - "invariant": "^2.2.2" - } - }, - "select": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", - "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" - }, - "selenium-webdriver": { - "version": "4.0.0-alpha.1", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.1.tgz", - "integrity": "sha512-z88rdjHAv3jmTZ7KSGUkTvo4rGzcDGMq0oXWHNIDK96Gs31JKVdu9+FMtT4KBrVoibg8dUicJDok6GnqqttO5Q==", - "dev": true, - "requires": { - "jszip": "^3.1.3", - "rimraf": "^2.5.4", - "tmp": "0.0.30", - "xml2js": "^0.4.17" - } - }, - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - }, - "send": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", - "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.1", - "destroy": "~1.0.4", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.3.1" - }, - "dependencies": { - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" - }, - "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" - } - } - }, - "sendy-api": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/sendy-api/-/sendy-api-0.1.0.tgz", - "integrity": "sha1-YhB/uVly1jBgyHAgQYM4gkfVeBA=", - "requires": { - "request": "2.34.0" - }, - "dependencies": { - "asn1": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", - "integrity": "sha1-VZvhg3bQik7E2+gId9J4GGObLfc=", - "optional": true - }, - "assert-plus": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", - "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=", - "optional": true - }, - "async": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", - "optional": true - }, - "aws-sign2": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", - "integrity": "sha1-xXED96F/wDfwLXwuZLYC6iI/fWM=", - "optional": true - }, - "boom": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", - "integrity": "sha1-emNune1O/O+xnO9JR6PGffrukRs=", - "requires": { - "hoek": "0.9.x" - } - }, - "combined-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", - "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=", - "optional": true, - "requires": { - "delayed-stream": "0.0.5" - } - }, - "cryptiles": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", - "integrity": "sha1-7ZH/HxetE9N0gohZT4pIoNJvMlw=", - "optional": true, - "requires": { - "boom": "0.4.x" - } - }, - "delayed-stream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", - "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8=", - "optional": true - }, - "forever-agent": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz", - "integrity": "sha1-bQ4JxJIflKJ/Y9O0nF/v8epMUTA=" - }, - "form-data": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", - "integrity": "sha1-kavXiKupcCsaq/qLwBAxoqyeOxI=", - "optional": true, - "requires": { - "async": "~0.9.0", - "combined-stream": "~0.0.4", - "mime": "~1.2.11" - } - }, - "hawk": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", - "integrity": "sha1-uQuxaYByhUEdp//LjdJZhQLTtS0=", - "optional": true, - "requires": { - "boom": "0.4.x", - "cryptiles": "0.2.x", - "hoek": "0.9.x", - "sntp": "0.2.x" - } - }, - "hoek": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", - "integrity": "sha1-PTIkYrrfB3Fup+uFuviAec3c5QU=" - }, - "http-signature": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", - "integrity": "sha1-T72sEyVZqoMjEh5UB3nAoBKyfmY=", - "optional": true, - "requires": { - "asn1": "0.1.11", - "assert-plus": "^0.1.5", - "ctype": "0.5.3" - } - }, - "mime": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", - "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=" - }, - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" - }, - "oauth-sign": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", - "integrity": "sha1-y1QPk7srIqfVlBaRoojWDo6pOG4=", - "optional": true - }, - "qs": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz", - "integrity": "sha1-bgFQmP9RlouKPIGQAdXyyJvEsQc=" - }, - "request": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.34.0.tgz", - "integrity": "sha1-tdi5UmrdSi1GKfTUFxJFc5lkRa4=", - "requires": { - "aws-sign2": "~0.5.0", - "forever-agent": "~0.5.0", - "form-data": "~0.1.0", - "hawk": "~1.0.0", - "http-signature": "~0.10.0", - "json-stringify-safe": "~5.0.0", - "mime": "~1.2.9", - "node-uuid": "~1.4.0", - "oauth-sign": "~0.3.0", - "qs": "~0.6.0", - "tough-cookie": ">=0.12.0", - "tunnel-agent": "~0.3.0" - } - }, - "sntp": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", - "integrity": "sha1-+4hfGLDzqtGJ+CSGJTa87ux1CQA=", - "optional": true, - "requires": { - "hoek": "0.9.x" - } - }, - "tunnel-agent": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz", - "integrity": "sha1-rWgbaPUyGtKCfEz7G31d8s/pQu4=", - "optional": true - } - } - }, - "serve-static": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", - "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", - "requires": { - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.1" - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" - }, - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" - }, - "shallowequal": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.0.2.tgz", - "integrity": "sha512-zlVXeVUKvo+HEv1e2KQF/csyeMKx2oHvatQ9l6XjCUj3agvC8XGf6R9HvIPDSmp8FNPvx7b5kaEJTRi7CqxtEw==" - }, - "shelljs": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", - "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", - "dev": true, - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "simpl-schema": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/simpl-schema/-/simpl-schema-1.4.2.tgz", - "integrity": "sha512-1zahMdjUnNVQMPKW6J25fd8LGEJ6bQLkVy8x9axv/gaCSeoO6eoXCM/jgz/Tj+n8AMHbgHwsYCKPirhFZ1OC7Q==", - "requires": { - "clone": "^2.1.1", - "extend": "^3.0.1", - "lodash.every": "^4.6.0", - "lodash.find": "^4.6.0", - "lodash.findwhere": "^3.1.0", - "lodash.includes": "^4.3.0", - "lodash.isempty": "^4.4.0", - "lodash.isobject": "^3.0.2", - "lodash.omit": "^4.5.0", - "lodash.pick": "^4.4.0", - "lodash.union": "^4.6.0", - "lodash.uniq": "^4.5.0", - "message-box": "^0.2.0", - "mongo-object": "^0.1.2" - } - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", - "dev": true - }, - "slick": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz", - "integrity": "sha1-vQSN23TefRymkV+qSldXCzVQwtc=" - }, - "sntp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", - "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", - "requires": { - "hoek": "4.x.x" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "spdx-correct": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", - "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", - "dev": true, - "requires": { - "spdx-license-ids": "^1.0.2" - } - }, - "spdx-expression-parse": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", - "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", - "dev": true - }, - "spdx-license-ids": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", - "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", - "dev": true - }, - "speakingurl": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-9.0.0.tgz", - "integrity": "sha1-qZSUBBYn8x1tFMOLVx/6Oi7JW9o=" - }, - "sprintf-js": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz", - "integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=" - }, - "srcset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/srcset/-/srcset-1.0.0.tgz", - "integrity": "sha1-pWad4StC87HV6D7QPHEEb8SPQe8=", - "requires": { - "array-uniq": "^1.0.2", - "number-is-nan": "^1.0.0" - } - }, - "sshpk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "tweetnacl": "~0.14.0" - } - }, - "stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha1-1PM6tU6OOHeLDKXP07OvsS22hiA=", - "dev": true - }, - "state-toggle": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.0.tgz", - "integrity": "sha1-0g+aYWu08MO5i5GSLSW2QKorxCU=" - }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" - }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "dev": true - }, - "stream-line-wrapper": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/stream-line-wrapper/-/stream-line-wrapper-0.1.1.tgz", - "integrity": "sha1-Pivh02jGNW+Qru9keGaD8+7j7qc=", - "requires": { - "async": "~0.2.10" - } - }, - "streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string.prototype.repeat": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-0.2.0.tgz", - "integrity": "sha1-q6Nt4I3O5qWjN9SbLqHaGyj8Ds8=" - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "stripe": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/stripe/-/stripe-4.25.0.tgz", - "integrity": "sha512-sSRPSQ4BTSbdcevVSrtIJzlOCTIAXm8T38DE4zPL6ysYpIWGfIBdo2XnhouLK12/6cuLvaEInlfCZQgoEVzXpQ==", - "requires": { - "bluebird": "^2.10.2", - "lodash.isplainobject": "^4.0.6", - "object-assign": "^4.1.0", - "qs": "~6.0.4" - }, - "dependencies": { - "qs": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.0.4.tgz", - "integrity": "sha1-UQGdhHIMk5uCc36EVWp4Izjs6ns=" - } - } - }, - "styled-components": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-2.4.0.tgz", - "integrity": "sha512-bLW0/lQxTgJ0y+TEllctly+/B0Hz2N82e5AhubP+FIVPSisyOzyFnZzWdqRml7RDwRCsT+EGNN8YYa0VFutT+w==", - "requires": { - "buffer": "^5.0.3", - "css-to-react-native": "^2.0.3", - "fbjs": "^0.8.9", - "hoist-non-react-statics": "^1.2.0", - "is-plain-object": "^2.0.1", - "prop-types": "^15.5.4", - "stylis": "^3.4.0", - "supports-color": "^3.2.3" - }, - "dependencies": { - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" - }, - "hoist-non-react-statics": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", - "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs=" - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "stylis": { - "version": "3.4.5", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.4.5.tgz", - "integrity": "sha512-xxfO3FlxEKcNL1gTX4Tb/tyDLOlUcWCQopceIoQe7sBsX81Na83PNba7DFqMqgb9Rn1VjHkSAWdS9uhL/NVo+Q==" - }, - "subscriptions-transport-ws": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.15.tgz", - "integrity": "sha512-f9eBfWdHsePQV67QIX+VRhf++dn1adyC/PZHP6XI5AfKnZ4n0FW+v5omxwdHVpd4xq2ZijaHEcmlQrhBY79ZWQ==", - "requires": { - "backo2": "^1.0.2", - "eventemitter3": "^3.1.0", - "iterall": "^1.2.1", - "symbol-observable": "^1.0.4", - "ws": "^5.2.0" - }, - "dependencies": { - "iterall": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz", - "integrity": "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==" - }, - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "requires": { - "async-limiter": "~1.0.0" - } - } - } - }, - "superagent": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.2.tgz", - "integrity": "sha512-gVH4QfYHcY3P0f/BZzavLreHW3T1v7hG9B+hpMQotGQqurOvhv87GcMCd6LWySmBuf+BDR44TQd0aISjVHLeNQ==", - "requires": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.1.1", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.0.5" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - } - } - }, - "superagent-retry": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/superagent-retry/-/superagent-retry-0.6.0.tgz", - "integrity": "sha1-5Js1ypbA47HQ4/SWBRNt8OCgKLc=" - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "requires": { - "has-flag": "^2.0.0" - } - }, - "svg-pan-zoom": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/svg-pan-zoom/-/svg-pan-zoom-3.6.0.tgz", - "integrity": "sha512-ZBEL2z/n/W2fLLFzn3NTQgd+7QEfbrKvKmu29U3qvMflmJgLaWkwKbOzWJYESFidTiGYMHkijAKmY6m64moyYg==" - }, - "symbol-observable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.1.0.tgz", - "integrity": "sha512-dQoid9tqQ+uotGhuTKEY11X4xhyYePVnqGSoSm3OGKh2E8LZ6RPULp1uXTctk33IeERlrRJYoVSBglsL05F5Uw==" - }, - "symbol-tree": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", - "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=" - }, - "table": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", - "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", - "dev": true, - "requires": { - "ajv": "^4.7.0", - "ajv-keywords": "^1.0.0", - "chalk": "^1.1.1", - "lodash": "^4.0.0", - "slice-ansi": "0.0.4", - "string-width": "^2.0.0" - }, - "dependencies": { - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true, - "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" - } - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "templayed": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/templayed/-/templayed-0.2.3.tgz", - "integrity": "sha1-RwbfYlvGrs2Gt8n2sPtUi5XN92k=" - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "throwback": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/throwback/-/throwback-1.1.1.tgz", - "integrity": "sha1-8AfnwXYEptFtegfEGqDo/txhhKQ=", - "requires": { - "any-promise": "^1.3.0" - } - }, - "timers-ext": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.2.tgz", - "integrity": "sha1-YcxHp2wavTGV8UUn+XjViulMUgQ=", - "requires": { - "es5-ext": "~0.10.14", - "next-tick": "1" - }, - "dependencies": { - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - } - } - }, - "tiny-emitter": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.0.2.tgz", - "integrity": "sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow==" - }, - "title-case-minors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/title-case-minors/-/title-case-minors-1.0.0.tgz", - "integrity": "sha1-UfFwN8KUdHodHNpCS1AEyG2OsRU=" - }, - "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" - } - }, - "to-capital-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-capital-case/-/to-capital-case-1.0.0.tgz", - "integrity": "sha1-pXxQFP1aNyF88FCZ/4pCG7+cm38=", - "requires": { - "to-space-case": "^1.0.0" - } - }, - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - }, - "to-no-case": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", - "integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo=" - }, - "to-sentence-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-sentence-case/-/to-sentence-case-1.0.0.tgz", - "integrity": "sha1-xIO/NkdzflxzjvcAb+Ng1fmcVy4=", - "requires": { - "to-no-case": "^1.0.0" - } - }, - "to-space-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", - "integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=", - "requires": { - "to-no-case": "^1.0.0" - } - }, - "to-title-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-title-case/-/to-title-case-1.0.0.tgz", - "integrity": "sha1-rKiPidYGTeUBCKl86g20SCfoAGE=", - "requires": { - "escape-regexp-component": "^1.0.2", - "title-case-minors": "^1.0.0", - "to-capital-case": "^1.0.0", - "to-sentence-case": "^1.0.0" - } - }, - "tough-cookie": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", - "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", - "requires": { - "punycode": "^1.4.1" - } - }, - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", - "dev": true, - "requires": { - "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - } - } - }, - "trim": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, - "trim-trailing-lines": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.0.tgz", - "integrity": "sha1-eu+7eAjfnWafbaLkOMrIxGradoQ=" - }, - "trough": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.1.tgz", - "integrity": "sha1-qf2LA5Swro//guBjOgo2zK1bX4Y=" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "optional": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-is": { - "version": "1.6.15", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", - "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.15" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "ua-parser-js": { - "version": "0.7.17", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz", - "integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g==" - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "optional": true - }, - "uncontrollable": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-4.1.0.tgz", - "integrity": "sha1-4DWCkSUuGGUiLZCTmxny9J+Bwak=", - "requires": { - "invariant": "^2.1.0" - } - }, - "underscore": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", - "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" - }, - "underscore.string": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.4.tgz", - "integrity": "sha1-LCo/n4PmR2L9xF5s6sZRQoZCE9s=", - "requires": { - "sprintf-js": "^1.0.3", - "util-deprecate": "^1.0.2" - } - }, - "unherit": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.0.tgz", - "integrity": "sha1-a5qu379z3xdWrZ4xbdmBiFhAzX0=", - "requires": { - "inherits": "^2.0.1", - "xtend": "^4.0.1" - } - }, - "unified": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/unified/-/unified-6.1.6.tgz", - "integrity": "sha512-pW2f82bCIo2ifuIGYcV12fL96kMMYgw7JKVEgh7ODlrM9rj6vXSY3BV+H6lCcv1ksxynFf582hwWLnA1qRFy4w==", - "requires": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^1.1.0", - "trough": "^1.0.0", - "vfile": "^2.0.0", - "x-is-function": "^1.0.4", - "x-is-string": "^0.1.0" - } - }, - "unist-util-is": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.1.tgz", - "integrity": "sha1-DDEmKeP5YMZukx6BLT2A53AQlHs=" - }, - "unist-util-remove-position": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.1.tgz", - "integrity": "sha1-WoXBVV/BugwQG4ZwfRXlD6TIcbs=", - "requires": { - "unist-util-visit": "^1.1.0" - } - }, - "unist-util-stringify-position": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.1.tgz", - "integrity": "sha1-PMvcU2ee7W7PN3fdf14yKcG2qjw=" - }, - "unist-util-visit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.3.0.tgz", - "integrity": "sha512-9ntYcxPFtl44gnwXrQKZ5bMqXMY0ZHzUpqMFiU4zcc8mmf/jzYm8GhYgezuUlX4cJIM1zIDYaO6fG/fI+L6iiQ==", - "requires": { - "unist-util-is": "^2.1.1" - } - }, - "universal-cookie": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-2.1.5.tgz", - "integrity": "sha512-Yp+hn1tlgahtJvPkBwIx9yBTQZt2RC2bPjvmhkZhcHlXq6z0Plg83zL6TYJHDFtlTSnUgLMglyQuvDFvfUQb5w==", - "requires": { - "cookie": "^0.3.1", - "object-assign": "^4.1.0" - } - }, - "universal-cookie-express": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/universal-cookie-express/-/universal-cookie-express-2.1.5.tgz", - "integrity": "sha512-VdrUEDQvMTwrs4sSJyPcSFkUqHOaIBw0HcLaskzygrLV3cB5GLSBBg1Wr4+mGg4DRaKuQtDaeTRrakPPwJFzSw==", - "requires": { - "universal-cookie": "^2.1.5" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" - } - } - }, - "user-home": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", - "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", - "dev": true, - "requires": { - "os-homedir": "^1.0.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "uuid": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" - }, - "validate-npm-package-license": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", - "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", - "dev": true, - "requires": { - "spdx-correct": "~1.0.0", - "spdx-expression-parse": "~1.0.0" - } - }, - "value-equal": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", - "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "vfile": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz", - "integrity": "sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w==", - "requires": { - "is-buffer": "^1.1.4", - "replace-ext": "1.0.0", - "unist-util-stringify-position": "^1.0.0", - "vfile-message": "^1.0.0" - } - }, - "vfile-location": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.2.tgz", - "integrity": "sha1-02dcWch3SY5JK0dW/2Xkrxp1IlU=" - }, - "vfile-message": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.0.0.tgz", - "integrity": "sha512-HPREhzTOB/sNDc9/Mxf8w0FmHnThg5CRSJdR9VRFkD2riqYWs+fuXlj5z8mIpv2LrD7uU41+oPWFOL4Mjlf+dw==", - "requires": { - "unist-util-stringify-position": "^1.1.1" - } - }, - "viz.js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/viz.js/-/viz.js-2.0.0.tgz", - "integrity": "sha512-OzUX9NWBc4u9QjFjVZYXGf7evbDHD8D9YcnVk9qEgrpYzWmeX+9Cov0n2KxbIidRRAef8OXwGrPfwnWubasKQg==" - }, - "w3c-hr-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", - "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", - "dev": true, - "requires": { - "browser-process-hrtime": "^0.1.2" - } - }, - "warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", - "requires": { - "loose-envify": "^1.0.0" - } - }, - "web-resource-inliner": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-1.2.1.tgz", - "integrity": "sha1-wCMyrZhe0A2kwjEMBpn7LlupZ18=", - "requires": { - "async": "^0.9.0", - "clean-css": "1.1.7", - "cli-color": "^0.3.2", - "datauri": "~0.2.0", - "lodash": "^3.10.1", - "request": "^2.49.0", - "uglify-js": "^2.4.1", - "xtend": "^4.0.0" - }, - "dependencies": { - "async": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" - }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" - } - } - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, - "webpack-require-weak": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/webpack-require-weak/-/webpack-require-weak-1.0.1.tgz", - "integrity": "sha1-pqjmCHG+u+WwhakVqwr2M6QSQz8=", - "requires": { - "is-webpack-bundle": "^1.0.0" - } - }, - "whatwg-encoding": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz", - "integrity": "sha512-jLBwwKUhi8WtBfsMQlL4bUUcT8sMkAtQinscJAe/M4KHCkHuUJAF6vuB0tueNIw4c8ziO6AkRmgY+jL3a0iiPw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.19" - } - }, - "whatwg-fetch": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz", - "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=" - }, - "whatwg-mimetype": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz", - "integrity": "sha512-FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC+m0Mew==", - "dev": true - }, - "whatwg-url": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", - "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "whatwg-url-compat": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/whatwg-url-compat/-/whatwg-url-compat-0.6.5.tgz", - "integrity": "sha1-AImBEa9om7CXVBzVpFymyHmERb8=", - "optional": true, - "requires": { - "tr46": "~0.0.1" - }, - "dependencies": { - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", - "optional": true - } - } - }, - "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", - "requires": { - "isexe": "^2.0.0" - } - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" - }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, - "ws": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz", - "integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0" - } - }, - "x-is-function": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/x-is-function/-/x-is-function-1.0.4.tgz", - "integrity": "sha1-XSlNw9Joy90GJYDgxd93o5HR+h4=" - }, - "x-is-string": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", - "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=" - }, - "xml": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", - "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=" - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", - "dev": true, - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" - } - }, - "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", - "dev": true - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } - }, - "yauzl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", - "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", - "dev": true, - "requires": { - "fd-slicer": "~1.0.1" - } - }, - "zen-observable": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.8.tgz", - "integrity": "sha512-HnhhyNnwTFzS48nihkCZIJGsWGFcYUz+XPDlPK5W84Ifji8SksC6m7sQWOf8zdCGhzQ4tDYuMYGu5B0N1dXTtg==" - }, - "zen-observable-ts": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.10.tgz", - "integrity": "sha512-5vqMtRggU/2GhePC9OU4sYEWOdvmayp2k3gjPf4F0mXwB3CSbbNznfDUvDJx9O2ZTa1EIXdJhPchQveFKwNXPQ==", - "requires": { - "zen-observable": "^0.8.0" - } - } - } -} diff --git a/package.json b/package.json index 4e809d255..b82e53e0b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Vulcan", - "version": "1.12.8", + "version": "1.12.13", "engines": { "npm": "^3.0" }, @@ -8,7 +8,15 @@ "start": "meteor --settings settings.json", "lint": "eslint --cache --ext .jsx,js packages", "test-unit": "TEST_WATCH=1 meteor test-packages ./packages/* --port 3002 --driver-package meteortesting:mocha --raw-logs", - "test": "npm run test-unit" + "test": "npm run test-unit", + "prettier": "node ./.vulcan/prettier/index.js write-changed", + "prettier-all": "node ./.vulcan/prettier/index.js write" + }, + "husky": { + "hooks": { + "pre-commit": "npm run lint", + "pre-push": "npm run prettier" + } }, "dependencies": { "@babel/runtime": "7.0.0-beta.55", @@ -17,12 +25,13 @@ "apollo-client": "^2.4.2", "apollo-engine": "^0.5.4", "apollo-errors": "^1.4.0", + "apollo-link-error": "^1.1.5", "apollo-link-schema": "^1.1.1", "apollo-link-state": "^0.4.2", "apollo-server": "^2.1.0", "apollo-server-express": "^2.1.0", "babel-runtime": "^6.26.0", - "bcrypt": "^2.0.1", + "bcrypt": "^3.0.2", "body-parser": "^1.18.2", "chalk": "2.2.0", "classnames": "^2.2.3", @@ -52,11 +61,10 @@ "import": "0.0.6", "intl": "^1.2.4", "intl-locales-supported": "^1.0.0", - "juice": "^1.11.0", + "juice": "^5.1.0", "lodash": "^4.17.10", "mailchimp": "^1.1.6", "marked": "^0.3.9", - "metascraper": "^1.0.6", "meteor-node-stubs": "^0.2.3", "mingo": "^2.2.0", "moment": "^2.13.0", @@ -64,7 +72,7 @@ "react": "^16.2.0", "react-addons-pure-render-mixin": "^15.4.1", "react-apollo": "^2.2.4", - "react-bootstrap": "^0.32.0", + "react-bootstrap": "^1.0.0-beta.3", "react-bootstrap-datetimepicker": "0.0.22", "react-cookie": "^2.1.4", "react-datetime": "^2.11.1", @@ -85,7 +93,6 @@ "redux": "^3.6.0", "rss": "^1.2.1", "sanitize-html": "^1.16.3", - "sendy-api": "^0.1.0", "simpl-schema": "^1.4.2", "speakingurl": "^9.0.0", "stripe": "^4.23.1", @@ -98,9 +105,10 @@ "devDependencies": { "autoprefixer": "^6.3.6", "babel-eslint": "^7.0.0", + "babylon": "^6.18.0", "chromedriver": "^2.40.0", "enzyme": "^3.3.0", - "enzyme-adapter-react-16": "^1.1.1", + "enzyme-adapter-react-16.3": "^1.4.0", "eslint": "^3.19.0", "eslint-config-airbnb": "^13.0.0", "eslint-config-meteor": "0.0.9", @@ -113,8 +121,11 @@ "eslint-plugin-prettier": "^2.5.0", "eslint-plugin-react": "^6.7.1", "expect": "^23.4.0", + "glob": "^7.1.3", + "husky": "^1.2.0", "jsdom": "^11.11.0", "jsdom-global": "^3.0.2", + "prettier": "^1.15.2", "selenium-webdriver": "^4.0.0-alpha.1" }, "postcss": { diff --git a/packages/_boilerplate-generator/package.js b/packages/_boilerplate-generator/package.js index 8bb42e7fa..7d0862e02 100644 --- a/packages/_boilerplate-generator/package.js +++ b/packages/_boilerplate-generator/package.js @@ -1,11 +1,11 @@ Package.describe({ - summary: "Generates the boilerplate html from program's manifest", - version: '1.5.0', + summary: 'Generates the boilerplate html from program\'s manifest', + version: '1.6.0', name: 'boilerplate-generator' }); Npm.depends({ - "combined-stream2": "1.1.2" + 'combined-stream2': '1.1.2' }); Package.onUse(api => { diff --git a/packages/vulcan-accounts/imports/emailTemplates.js b/packages/vulcan-accounts/imports/emailTemplates.js new file mode 100644 index 000000000..d69b4a8cc --- /dev/null +++ b/packages/vulcan-accounts/imports/emailTemplates.js @@ -0,0 +1,12 @@ +import {Accounts} from 'meteor/accounts-base'; +import {getSetting} from 'meteor/vulcan:core'; + +// the emailTemplates are made available by accounts-password, which we don't want to depend on +if (Package['accounts-password']) { + Accounts.emailTemplates.siteName = getSetting('public.title', ''); + Accounts.emailTemplates.from = + getSetting('public.title', '') + + ' <' + + getSetting('defaultEmail', 'no-reply@example.com') + + '>'; +} diff --git a/packages/vulcan-accounts/imports/helpers.js b/packages/vulcan-accounts/imports/helpers.js index 955c780ed..a6afd669d 100755 --- a/packages/vulcan-accounts/imports/helpers.js +++ b/packages/vulcan-accounts/imports/helpers.js @@ -80,7 +80,7 @@ export function validatePassword(password = '', showMessage, clearMessage){ if (password.length >= Accounts.ui._options.minimumPasswordLength) { return true; } else { - const errMsg = 'accounts.error_minchar' + const errMsg = 'accounts.error_minchar'; showMessage(errMsg, 'warning', false, 'password'); return false; } diff --git a/packages/vulcan-accounts/imports/ui/components/EnrollAccount.jsx b/packages/vulcan-accounts/imports/ui/components/EnrollAccount.jsx index f7252d85e..012f65456 100644 --- a/packages/vulcan-accounts/imports/ui/components/EnrollAccount.jsx +++ b/packages/vulcan-accounts/imports/ui/components/EnrollAccount.jsx @@ -29,7 +29,7 @@ class AccountsEnrollAccount extends PureComponent { AccountsEnrollAccount.contextTypes = { intl: intlShape -} +}; AccountsEnrollAccount.propsTypes = { currentUser: PropTypes.object, diff --git a/packages/vulcan-accounts/imports/ui/components/Field.jsx b/packages/vulcan-accounts/imports/ui/components/Field.jsx index 89288fd9c..1afecc473 100755 --- a/packages/vulcan-accounts/imports/ui/components/Field.jsx +++ b/packages/vulcan-accounts/imports/ui/components/Field.jsx @@ -7,7 +7,7 @@ const autocompleteValues = { 'usernameOrEmail': 'email', 'email': 'email', 'password': 'current-password' -} +}; export class AccountsField extends PureComponent { constructor(props) { @@ -75,4 +75,4 @@ AccountsField.propTypes = { onChange: PropTypes.func }; -registerComponent('AccountsField', AccountsField) \ No newline at end of file +registerComponent('AccountsField', AccountsField); \ No newline at end of file diff --git a/packages/vulcan-accounts/imports/ui/components/LoginFormInner.jsx b/packages/vulcan-accounts/imports/ui/components/LoginFormInner.jsx index a696b37bf..666559d81 100644 --- a/packages/vulcan-accounts/imports/ui/components/LoginFormInner.jsx +++ b/packages/vulcan-accounts/imports/ui/components/LoginFormInner.jsx @@ -35,9 +35,9 @@ export class AccountsLoginFormInner extends TrackerComponent { return () => { props.client.resetStore().then(() => { hook(); - }) - } - } + }); + }; + }; const postLogInAndThen = hook => { return () => { @@ -47,9 +47,9 @@ export class AccountsLoginFormInner extends TrackerComponent { } else { // or else execute the hook hook(); } - }) - } - } + }); + }; + }; const doNothing = () => {}; @@ -1008,16 +1008,16 @@ export class AccountsLoginFormInner extends TrackerComponent { AccountsLoginFormInner.propTypes = { showSignInLink: PropTypes.bool, showSignUpLink: PropTypes.bool, -} +}; AccountsLoginFormInner.defaultProps = { showSignInLink: true, showSignUpLink: true, redirect: true, -} +}; AccountsLoginFormInner.contextTypes = { intl: intlShape -} +}; registerComponent('AccountsLoginFormInner', AccountsLoginFormInner, withCurrentUser, withApollo); diff --git a/packages/vulcan-accounts/imports/ui/components/ResetPassword.jsx b/packages/vulcan-accounts/imports/ui/components/ResetPassword.jsx index b7c829e3a..10f7a7aa5 100644 --- a/packages/vulcan-accounts/imports/ui/components/ResetPassword.jsx +++ b/packages/vulcan-accounts/imports/ui/components/ResetPassword.jsx @@ -29,7 +29,7 @@ class AccountsResetPassword extends PureComponent { AccountsResetPassword.contextTypes = { intl: intlShape -} +}; AccountsResetPassword.propsTypes = { currentUser: PropTypes.object, diff --git a/packages/vulcan-accounts/imports/ui/components/StateSwitcher.jsx b/packages/vulcan-accounts/imports/ui/components/StateSwitcher.jsx index e5718a0c8..dac431695 100644 --- a/packages/vulcan-accounts/imports/ui/components/StateSwitcher.jsx +++ b/packages/vulcan-accounts/imports/ui/components/StateSwitcher.jsx @@ -13,7 +13,7 @@ export class AccountsStateSwitcher extends React.Component { this.state = { formState: props.formState - } + }; } switchToSignUp = (event) => { @@ -92,7 +92,7 @@ export class AccountsStateSwitcher extends React.Component { switchToSignOut, cancelResetPassword, switchToProfile, - } + }; return ( ); diff --git a/packages/vulcan-accounts/imports/ui/components/TrackerComponent.jsx b/packages/vulcan-accounts/imports/ui/components/TrackerComponent.jsx index b9f4144c7..d4ab255b4 100644 --- a/packages/vulcan-accounts/imports/ui/components/TrackerComponent.jsx +++ b/packages/vulcan-accounts/imports/ui/components/TrackerComponent.jsx @@ -21,11 +21,11 @@ class TrackerComponent extends React.Component { autorun(fn) { return this.__comps.push(Tracker.autorun(c => { this.__live = true; fn(c); this.__live = false; - }))} + }));} componentDidUpdate() { !this.__live && this.__comps.forEach(c => { c.invalidated = c.stopped = false; !c.invalidate(); - })} + });} subscriptionsReady() { return !Object.keys(this.__subs).some(id => !this.__subs[id].ready()); diff --git a/packages/vulcan-accounts/imports/ui/components/VerifyEmail.jsx b/packages/vulcan-accounts/imports/ui/components/VerifyEmail.jsx index 346764f0f..5e9117913 100644 --- a/packages/vulcan-accounts/imports/ui/components/VerifyEmail.jsx +++ b/packages/vulcan-accounts/imports/ui/components/VerifyEmail.jsx @@ -6,11 +6,11 @@ import { intlShape } from 'meteor/vulcan:i18n'; class AccountsVerifyEmail extends PureComponent { constructor(props) { - super(props) + super(props); this.state = { pending: true, error: null - } + }; } componentDidMount() { @@ -32,7 +32,7 @@ class AccountsVerifyEmail extends PureComponent { render() { if(this.state.pending) { - return + return ; } else if(this.state.error) { return (
@@ -51,7 +51,7 @@ class AccountsVerifyEmail extends PureComponent { AccountsVerifyEmail.contextTypes = { intl: intlShape -} +}; AccountsVerifyEmail.propsTypes = { currentUser: PropTypes.object, diff --git a/packages/vulcan-accounts/main_server.js b/packages/vulcan-accounts/main_server.js index c5158720c..c2281cbb8 100755 --- a/packages/vulcan-accounts/main_server.js +++ b/packages/vulcan-accounts/main_server.js @@ -4,6 +4,7 @@ import './imports/components.js'; import './imports/login_session.js'; import './imports/routes.js'; import './imports/oauth_config.js'; +import './imports/emailTemplates.js'; import { redirect, STATES } from './imports/helpers.js'; import './imports/api/server/servicesListPublication.js'; diff --git a/packages/vulcan-accounts/package.js b/packages/vulcan-accounts/package.js index d4e097183..7c7ed3c57 100755 --- a/packages/vulcan-accounts/package.js +++ b/packages/vulcan-accounts/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'vulcan:accounts', - version: '1.12.8', + version: '1.12.13', summary: 'Accounts UI for React in Meteor 1.3+', git: 'https://github.com/studiointeract/accounts-ui', documentation: 'README.md' @@ -9,7 +9,7 @@ Package.describe({ Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use('vulcan:core@1.12.8'); + api.use('vulcan:core@1.12.13'); api.use('ecmascript'); api.use('tracker'); diff --git a/packages/vulcan-admin/lib/components/AdminHome.jsx b/packages/vulcan-admin/lib/components/AdminHome.jsx index 9b05e762e..bc26d8b00 100644 --- a/packages/vulcan-admin/lib/components/AdminHome.jsx +++ b/packages/vulcan-admin/lib/components/AdminHome.jsx @@ -19,6 +19,6 @@ const AdminHome = ({ currentUser }) => showEdit={true} /> -
+ ; export default withCurrentUser(AdminHome); \ No newline at end of file diff --git a/packages/vulcan-admin/lib/components/users/columns/AdminUsersActions.jsx b/packages/vulcan-admin/lib/components/users/columns/AdminUsersActions.jsx index ec35ae649..e0534d64e 100644 --- a/packages/vulcan-admin/lib/components/users/columns/AdminUsersActions.jsx +++ b/packages/vulcan-admin/lib/components/users/columns/AdminUsersActions.jsx @@ -9,14 +9,14 @@ const AdminUsersActions = ({ document: user, removeMutation }) =>{ if (confirm(`Delete user ${Users.getDisplayName(user)}?`)) { removeMutation({documentId: user._id}); } - } + }; - return Delete -} + return Delete; +}; const removeOptions = { collection: Users -} +}; export default withRemove(removeOptions)(AdminUsersActions); diff --git a/packages/vulcan-admin/lib/components/users/columns/AdminUsersCreated.jsx b/packages/vulcan-admin/lib/components/users/columns/AdminUsersCreated.jsx index 683d023b5..216be3711 100644 --- a/packages/vulcan-admin/lib/components/users/columns/AdminUsersCreated.jsx +++ b/packages/vulcan-admin/lib/components/users/columns/AdminUsersCreated.jsx @@ -5,6 +5,6 @@ import moment from 'moment'; const AdminUsersCreated = ({ document: user }) =>
{moment(new Date(user.createdAt)).format('MM/DD/YY')} -
+ ; export default AdminUsersCreated; \ No newline at end of file diff --git a/packages/vulcan-admin/lib/components/users/columns/AdminUsersEmail.jsx b/packages/vulcan-admin/lib/components/users/columns/AdminUsersEmail.jsx index bdebace47..7dd1b41e0 100644 --- a/packages/vulcan-admin/lib/components/users/columns/AdminUsersEmail.jsx +++ b/packages/vulcan-admin/lib/components/users/columns/AdminUsersEmail.jsx @@ -3,6 +3,6 @@ import Users from 'meteor/vulcan:users'; import { Components } from 'meteor/vulcan:core'; const AdminUsersEmail = ({ document: user }) => - {Users.getEmail(user)} + {Users.getEmail(user)}; export default AdminUsersEmail; \ No newline at end of file diff --git a/packages/vulcan-admin/lib/components/users/columns/AdminUsersName.jsx b/packages/vulcan-admin/lib/components/users/columns/AdminUsersName.jsx index e63429885..c56404761 100644 --- a/packages/vulcan-admin/lib/components/users/columns/AdminUsersName.jsx +++ b/packages/vulcan-admin/lib/components/users/columns/AdminUsersName.jsx @@ -13,6 +13,6 @@ const AdminUsersName = ({ document: user, flash }) => {_.rest(Users.getGroups(user)).map(group => {group})} - + ; export default AdminUsersName; \ No newline at end of file diff --git a/packages/vulcan-admin/package.js b/packages/vulcan-admin/package.js index 6f3a7254b..f69990003 100644 --- a/packages/vulcan-admin/package.js +++ b/packages/vulcan-admin/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:admin', summary: 'Vulcan components package', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -11,10 +11,10 @@ Package.onUse(function (api) { api.use([ - 'fourseven:scss@4.5.0', + 'fourseven:scss@4.10.0', 'dynamic-import@0.1.1', // Vulcan packages - 'vulcan:core@1.12.8', + 'vulcan:core@1.12.13', ]); diff --git a/packages/vulcan-cloudinary/lib/client/main.js b/packages/vulcan-cloudinary/lib/client/main.js index 8ae314aa6..ae66b4fc4 100644 --- a/packages/vulcan-cloudinary/lib/client/main.js +++ b/packages/vulcan-cloudinary/lib/client/main.js @@ -1,2 +1,2 @@ -export * from '../modules/index.js' -export * from './make_cloudinary.js' +export * from '../modules/index.js'; +export * from './make_cloudinary.js'; diff --git a/packages/vulcan-cloudinary/lib/client/make_cloudinary.js b/packages/vulcan-cloudinary/lib/client/make_cloudinary.js index 357179419..a748a7f25 100644 --- a/packages/vulcan-cloudinary/lib/client/make_cloudinary.js +++ b/packages/vulcan-cloudinary/lib/client/make_cloudinary.js @@ -2,4 +2,4 @@ import { addCustomFields } from '../modules/index.js'; export const makeCloudinary = ({collection, fieldName}) => { addCustomFields(collection); -} \ No newline at end of file +}; \ No newline at end of file diff --git a/packages/vulcan-cloudinary/lib/modules/custom_fields.js b/packages/vulcan-cloudinary/lib/modules/custom_fields.js index 2f334b71c..c343b23f0 100644 --- a/packages/vulcan-cloudinary/lib/modules/custom_fields.js +++ b/packages/vulcan-cloudinary/lib/modules/custom_fields.js @@ -41,7 +41,7 @@ export const addCustomFields = collection => { type: 'String', arguments: 'format: String', resolver: (document, {format}, context) => { - const image = format ? _.findWhere(document.cloudinaryUrls, {name: format}) : document.cloudinaryUrls[0] + const image = format ? _.findWhere(document.cloudinaryUrls, {name: format}) : document.cloudinaryUrls[0]; return image && image.url; } }, @@ -50,4 +50,4 @@ export const addCustomFields = collection => { ]); -} \ No newline at end of file +}; \ No newline at end of file diff --git a/packages/vulcan-cloudinary/lib/server/main.js b/packages/vulcan-cloudinary/lib/server/main.js index c86320aa6..22362d6dc 100644 --- a/packages/vulcan-cloudinary/lib/server/main.js +++ b/packages/vulcan-cloudinary/lib/server/main.js @@ -1,3 +1,3 @@ export * from './cloudinary.js'; export * from '../modules/index.js'; -export * from './make_cloudinary.js' +export * from './make_cloudinary.js'; diff --git a/packages/vulcan-cloudinary/lib/server/make_cloudinary.js b/packages/vulcan-cloudinary/lib/server/make_cloudinary.js index 11ab1e948..d735d8d8e 100644 --- a/packages/vulcan-cloudinary/lib/server/make_cloudinary.js +++ b/packages/vulcan-cloudinary/lib/server/make_cloudinary.js @@ -39,4 +39,4 @@ export const makeCloudinary = ({collection, fieldName}) => { } addCallback(`${collection.options.collectionName.toLowerCase()}.edit.sync`, cacheImageOnEdit); -} \ No newline at end of file +}; \ No newline at end of file diff --git a/packages/vulcan-cloudinary/package.js b/packages/vulcan-cloudinary/package.js index 0a17504ee..c81242882 100644 --- a/packages/vulcan-cloudinary/package.js +++ b/packages/vulcan-cloudinary/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:cloudinary', summary: 'Vulcan file upload package.', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -10,7 +10,7 @@ Package.onUse(function (api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:core@1.12.8' + 'vulcan:core@1.12.13' ]); api.mainModule('lib/client/main.js', 'client'); diff --git a/packages/vulcan-core/lib/modules/callbacks.js b/packages/vulcan-core/lib/modules/callbacks.js index d0317dbae..bd317b87d 100644 --- a/packages/vulcan-core/lib/modules/callbacks.js +++ b/packages/vulcan-core/lib/modules/callbacks.js @@ -1,16 +1,15 @@ import { addCallback, getActions } from 'meteor/vulcan:lib'; /* - - Core callbacks - + + Core callbacks + */ /** * @summary Clear flash messages marked as seen when the route changes - * @param {Object} Item needed by `runCallbacks` to iterate on, unused here - * @param {Object} Redux store reference instantiated on the current connected client - * @param {Object} Apollo Client reference instantiated on the current connected client + * @param {Object} props + * @param {Object} props.client Apollo Client reference instantiated on the current connected client */ function RouterClearMessages(unusedItem, nextRoute, store, apolloClient) { // TODO Apollo2: clear error messages on route change @@ -18,4 +17,4 @@ function RouterClearMessages(unusedItem, nextRoute, store, apolloClient) { // return unusedItem; } -addCallback('router.onUpdate', RouterClearMessages); +addCallback('router.onUpdate.async', RouterClearMessages); diff --git a/packages/vulcan-core/lib/modules/components/App.jsx b/packages/vulcan-core/lib/modules/components/App.jsx index b8cc1df3d..2c22a7b9f 100644 --- a/packages/vulcan-core/lib/modules/components/App.jsx +++ b/packages/vulcan-core/lib/modules/components/App.jsx @@ -13,6 +13,7 @@ import PropTypes from 'prop-types'; import { IntlProvider, intlShape } from 'meteor/vulcan:i18n'; import withCurrentUser from '../containers/withCurrentUser.js'; import withUpdate from '../containers/withUpdate.js'; +import withSiteData from '../containers/withSiteData.js'; import { withApollo } from 'react-apollo'; import { withCookies } from 'react-cookie'; import moment from 'moment'; @@ -34,31 +35,47 @@ const RouteWithLayout = ({ layoutName, component, ...rest }) => ( /> ); +const DummyErrorCatcher = ({ children }) => children; + class App extends PureComponent { constructor(props) { super(props); if (props.currentUser) { runCallbacks('events.identify', props.currentUser); } - const locale = this.initLocale(); - this.state = { locale }; + const { locale, localeMethod } = this.initLocale(); + this.state = { locale, localeMethod }; moment.locale(locale); } + componentDidMount() { + runCallbacks('app.mounted', this.props); + } + initLocale = () => { let userLocale = ''; - const { currentUser, cookies } = this.props; + let localeMethod = ''; + const { currentUser, cookies, locale } = this.props; const availableLocales = Object.keys(Strings); + const detectedLocale = detectLocale(); - if (currentUser && currentUser.locale) { - // 1. if user is logged in, check for their preferred locale - userLocale = currentUser.locale; + if (locale) { + // 1. locale is passed through SSR process + // TODO: currently SSR locale is passed through cookies as a hack + userLocale = locale; + localeMethod = 'SSR'; } else if (cookies && cookies.get('locale')) { - // 2. else, look for a cookie + // 2. look for a cookie userLocale = cookies.get('locale'); - } else if (detectLocale()) { - // 3. else, check for browser settings - userLocale = detectLocale(); + localeMethod = 'cookie'; + } else if (currentUser && currentUser.locale) { + // 3. if user is logged in, check for their preferred locale + userLocale = currentUser.locale; + localeMethod = 'user'; + } else if (detectedLocale) { + // 4. else, check for browser settings + userLocale = detectedLocale; + localeMethod = 'browser'; } // if user locale is available, use it; else compare first two chars // of user locale with first two chars of available locales @@ -67,23 +84,29 @@ class App extends PureComponent { : availableLocales.find(locale => locale.slice(0, 2) === userLocale.slice(0, 2)); // 4. if user-defined locale is available, use it; else default to setting or `en-US` - return availableLocale ? availableLocale : getSetting('locale', 'en-US'); + if (availableLocale) { + return { locale: availableLocale, localeMethod }; + } else { + return { locale: getSetting('locale', 'en-US'), localeMethod: 'setting' }; + } }; - getLocale = (truncate = false) => { + getLocale = truncate => { return truncate ? this.state.locale.slice(0, 2) : this.state.locale; }; setLocale = async locale => { + const { cookies, updateUser, client, currentUser } = this.props; this.setState({ locale }); - this.props.cookies.set('locale', locale); + cookies.remove('locale', { path: '/' }); + cookies.set('locale', locale, { path: '/' }); // if user is logged in, change their `locale` profile property - if (this.props.currentUser) { - await this.props.updateUser({ selector: { documentId: this.props.currentUser._id }, data: { locale } }); + if (currentUser) { + await updateUser({ selector: { documentId: currentUser._id }, data: { locale } }); } moment.locale(locale); if (hasIntlFields) { - this.props.client.resetStore(); + client.resetStore(); } }; @@ -106,6 +129,12 @@ class App extends PureComponent { render() { const routeNames = Object.keys(Routes); + //const currentRoute = _.last(this.props.routes); + //const LayoutComponent = currentRoute.layoutName ? Components[currentRoute.layoutName] : Components.Layout; +// +// // if defined, use ErrorCatcher component to wrap layout contents +// const ErrorCatcher = Components.ErrorCatcher ? Components.ErrorCatcher : DummyErrorCatcher; + return (
@@ -121,11 +150,23 @@ class App extends PureComponent { // because it is the direct child of Switch ))} - // TODO Apollo2: figure out why this is not working + ) : ( )} + {/* + + + {this.props.currentUserLoading ? ( + + ) : this.props.children ? ( + {this.props.children} + ) : ( + + )} + + */}
); @@ -149,6 +190,6 @@ const updateOptions = { fragmentName: 'UsersCurrent', }; -registerComponent('App', App, withCurrentUser, [withUpdate, updateOptions], withApollo, withCookies); +registerComponent('App', App, withCurrentUser, withSiteData, [withUpdate, updateOptions], withApollo, withCookies); export default App; diff --git a/packages/vulcan-core/lib/modules/components/Avatar.jsx b/packages/vulcan-core/lib/modules/components/Avatar.jsx index c27c0aa17..fa3a99cad 100644 --- a/packages/vulcan-core/lib/modules/components/Avatar.jsx +++ b/packages/vulcan-core/lib/modules/components/Avatar.jsx @@ -10,7 +10,7 @@ const Avatar = ({ className, user, link, fallback }) => { const avatarClassNames = classNames('avatar', className); if (!user) { - return
{fallback}
+ return
{fallback}
; } const avatarUrl = user.avatarUrl || Users.avatar.getUrl(user); @@ -30,18 +30,18 @@ const Avatar = ({ className, user, link, fallback }) => { ); -} +}; Avatar.propTypes = { user: PropTypes.object, size: PropTypes.string, link: PropTypes.bool -} +}; Avatar.defaultProps = { size: 'medium', link: true -} +}; Avatar.displayName = 'Avatar'; diff --git a/packages/vulcan-core/lib/modules/components/Card.jsx b/packages/vulcan-core/lib/modules/components/Card.jsx index e4ab508cb..b8077398b 100644 --- a/packages/vulcan-core/lib/modules/components/Card.jsx +++ b/packages/vulcan-core/lib/modules/components/Card.jsx @@ -14,7 +14,7 @@ const getLabel = (field, fieldName, collection, intl) => { } else { return fieldName; } -} +}; const getTypeName = (field, fieldName, collection) => { const schema = collection && collection.simpleSchema()._schema; @@ -26,18 +26,18 @@ const getTypeName = (field, fieldName, collection) => { } else { return typeof field; } -} +}; const parseImageUrl = value => { const isImage = ['.png', '.jpg', '.gif'].indexOf(value.substr(-4)) !== -1 || ['.webp', '.jpeg'].indexOf(value.substr(-5)) !== -1; return isImage ? {value} : parseUrl(value); -} +}; const parseUrl = value => { - return value.slice(0, 4) === 'http' ? : ; -} + return value.slice(0,4) === 'http' ? : ; +}; const LimitedString = ({ string }) =>
@@ -45,12 +45,12 @@ const LimitedString = ({ string }) => {string.substr(0, 30)}… : {(string)} } -
+ ; export const getFieldValue = (value, typeName) => { if (typeof value === 'undefined' || value === null) { - return '' + return ''; } // JSX element @@ -76,7 +76,7 @@ export const getFieldValue = (value, typeName) => { return {value.toString()}; case 'Array': - return
    {value.map((item, index) =>
  1. {getFieldValue(item, typeof item)}
  2. )}
+ return
    {value.map((item, index) =>
  1. {getFieldValue(item, typeof item)}
  2. )}
; case 'Object': case 'object': @@ -92,7 +92,7 @@ export const getFieldValue = (value, typeName) => { default: return value.toString(); } -} +}; const getObject = object => { @@ -105,7 +105,7 @@ const getObject = object => { {user.displayName} - ) + ); } else { @@ -120,16 +120,16 @@ const getObject = object => { )} - ) + ); } -} +}; const CardItem = ({ label, value, typeName }) => {label} {getFieldValue(value, typeName)} - + ; const CardEdit = (props, context) => @@ -138,7 +138,7 @@ const CardEdit = (props, context) => - + ; CardEdit.contextTypes = { intl: intlShape }; @@ -150,7 +150,7 @@ const CardEditForm = ({ collection, document, closeModal }) => successCallback={document => { closeModal(); }} - /> + />; const Card = ({ title, className, collection, document, currentUser, fields, showEdit = true }, { intl }) => { @@ -181,10 +181,10 @@ Card.propTypes = { currentUser: PropTypes.object, fields: PropTypes.array, showEdit: PropTypes.bool -} +}; Card.contextTypes = { intl: intlShape -} +}; registerComponent('Card', Card); \ No newline at end of file diff --git a/packages/vulcan-core/lib/modules/components/Datatable.jsx b/packages/vulcan-core/lib/modules/components/Datatable.jsx index 72441f03e..2df6c2656 100644 --- a/packages/vulcan-core/lib/modules/components/Datatable.jsx +++ b/packages/vulcan-core/lib/modules/components/Datatable.jsx @@ -1,10 +1,12 @@ -import { registerComponent, Components, getCollection, Utils } from 'meteor/vulcan:lib'; +import { registerComponent, getCollection, Utils } from 'meteor/vulcan:lib'; import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import withCurrentUser from '../containers/withCurrentUser.js'; +import withComponents from '../containers/withComponents'; import withMulti from '../containers/withMulti.js'; import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; import { getFieldValue } from './Card.jsx'; +import _sortBy from 'lodash/sortBy'; /* @@ -21,6 +23,12 @@ const delay = (function(){ }; })(); +const getColumnName = column => ( + typeof column === 'string' + ? column + : column.label || column.name +); + class Datatable extends PureComponent { constructor() { @@ -30,7 +38,7 @@ class Datatable extends PureComponent { value: '', query: '', currentSort: {} - } + }; } toggleSort = column => { @@ -46,7 +54,7 @@ class Datatable extends PureComponent { } updateQuery(e) { - e.persist() + e.persist(); e.preventDefault(); this.setState({ value: e.target.value @@ -59,10 +67,11 @@ class Datatable extends PureComponent { } render() { + const { Components } = this.props; if (this.props.data) { // static JSON datatable - return ; + return ; } else { // dynamic datatable with data loading @@ -70,18 +79,23 @@ class Datatable extends PureComponent { const options = { collection, ...this.props.options - } + }; const DatatableWithMulti = withMulti(options)(Components.DatatableContents); const canInsert = collection.options && collection.options.mutations && collection.options.mutations.new && collection.options.mutations.new.check(this.props.currentUser); + + // add _id to orderBy when we want to sort a column, to avoid breaking the graphql() hoc; + // see https://github.com/VulcanJS/Vulcan/issues/2090#issuecomment-433860782 + // this.state.currentSort !== {} is always false, even when console.log(this.state.currentSort) displays {}. So we test on the length of keys for this object. + const orderBy = Object.keys(this.state.currentSort).length == 0 ? {} : { ...this.state.currentSort, _id: -1 }; return ( -
- - -
- ) + + + + + ); } } } @@ -98,41 +112,80 @@ Datatable.propTypes = { newFormOptions: PropTypes.object, editFormOptions: PropTypes.object, emptyState: PropTypes.object, -} + Components: PropTypes.object.isRequired, +}; Datatable.defaultProps = { showNew: true, showEdit: true, showSearch: true, -} -registerComponent('Datatable', Datatable, withCurrentUser); +}; +registerComponent({ name: 'Datatable', component: Datatable, hocs: [withCurrentUser, withComponents] }); +export default Datatable; +const DatatableLayout = ({ collectionName, children }) => ( +
+ {children} +
+); +registerComponent({ name: 'DatatableLayout', component: DatatableLayout }); /* DatatableAbove Component */ -const DatatableAbove = (props) => { - const { collection, currentUser, showSearch, showNew, canInsert, value, updateQuery, options, newFormOptions } = props; +const DatatableAbove = (props, { intl }) => { + const { collection, currentUser, showSearch, showNew, canInsert, + value, updateQuery, options, newFormOptions, Components } = props; return ( -
- {showSearch && } + + {showSearch && ( + + )} {showNew && canInsert && } -
- ) -} + + ); +}; +DatatableAbove.contextTypes = { + intl: intlShape, +}; +DatatableAbove.propTypes = { + Components: PropTypes.object.isRequired +}; registerComponent('DatatableAbove', DatatableAbove); + +const DatatableAboveSearchInput = (props) => ( + +); +registerComponent({ name: 'DatatableAboveSearchInput', component: DatatableAboveSearchInput }); + +const DatatableAboveLayout = ({ children }) => ( +
+ {children} +
+); +registerComponent({ name: 'DatatableAboveLayout', component: DatatableAboveLayout }); + /* DatatableHeader Component */ -const DatatableHeader = ({ collection, column, toggleSort, currentSort }, { intl }) => { +const DatatableHeader = ({ collection, column, toggleSort, currentSort, Components }, { intl }) => { - const columnName = typeof column === 'string' ? column : column.label || column.name; + const columnName = getColumnName(column); if (collection) { const schema = collection.simpleSchema()._schema; @@ -151,43 +204,55 @@ const DatatableHeader = ({ collection, column, toggleSort, currentSort }, { intl // if sortable is a string, use it as the name of the property to sort by. If it's just `true`, use column.name const sortPropertyName = typeof column.sortable === 'string' ? column.sortable : column.name; - return column.sortable ? : {formattedLabel}; + return column.sortable + ? + : {formattedLabel}; } else { const formattedLabel = intl.formatMessage({ id: columnName, defaultMessage: columnName }); - return {formattedLabel}; - + return ( + + {formattedLabel} + + ); } -} - +}; DatatableHeader.contextTypes = { intl: intlShape }; - +DatatableHeader.propTypes = { + Components: PropTypes.object.isRequired +}; registerComponent('DatatableHeader', DatatableHeader); +const DatatableHeaderCellLayout = ({ children, ...otherProps }) => ( + {children} +); +registerComponent({ name: 'DatatableHeaderCellLayout', component: DatatableHeaderCellLayout }); + const SortNone = () => - + ; const SortDesc = () => - + ; const SortAsc = () => - + ; const DatatableSorter = ({ name, label, toggleSort, currentSort }) => - -
{toggleSort(name)}}> +
{toggleSort(name);}}> {label} {!currentSort[name] ? ( @@ -200,7 +265,7 @@ const DatatableSorter = ({ name, label, toggleSort, currentSort }) => }
- + ; registerComponent('DatatableSorter', DatatableSorter); @@ -213,7 +278,10 @@ DatatableContents Component const DatatableContents = (props) => { // if no columns are provided, default to using keys of first array item - const { title, collection, results, columns, loading, loadMore, count, totalCount, networkStatus, showEdit, currentUser, emptyState, toggleSort, currentSort } = props; + const { title, collection, results, columns, loading, loadMore, + count, totalCount, networkStatus, showEdit, currentUser, emptyState, + toggleSort, currentSort, + Components } = props; if (loading) { return
; @@ -223,41 +291,89 @@ const DatatableContents = (props) => { const isLoadingMore = networkStatus === 2; const hasMore = totalCount > results.length; - + const sortedColumns = _sortBy(columns, column => column.order); return ( -
+ {title && } - - - - {_.sortBy(columns, column => column.order).map((column, index) => )} - {showEdit ? : null} - - - - {results.map((document, index) => )} - -
- {hasMore && -
- {isLoadingMore ? - : - {e.preventDefault(); loadMore();}}>Load More ({count}/{totalCount}) + + + { + sortedColumns + .map((column, index) => ( + ) + ) } -
+ {showEdit ? : null} + + + {results.map((document, index) => )} + + + {hasMore && + + {isLoadingMore + ? + : ( + { e.preventDefault(); loadMore(); }}> + Load More ({count}/{totalCount}) + + ) + } + } -
- ) -} + + ); +}; +DatatableContents.propTypes = { + Components: PropTypes.object.isRequired +}; registerComponent('DatatableContents', DatatableContents); +const DatatableContentsLayout = ({ children }) => ( +
+ {children} +
+); +registerComponent({ name: 'DatatableContentsLayout', component: DatatableContentsLayout }); +const DatatableContentsInnerLayout = ({ children }) => ( + + {children} +
+); +registerComponent({ name: 'DatatableContentsInnerLayout', component: DatatableContentsInnerLayout }); +const DatatableContentsHeadLayout = ({ children }) => ( + + + {children} + + +); +registerComponent({ name: 'DatatableContentsHeadLayout', component: DatatableContentsHeadLayout }); +const DatatableContentsBodyLayout = ({ children }) => ( + {children} +); +registerComponent({ name: 'DatatableContentsBodyLayout', component: DatatableContentsBodyLayout }); +const DatatableContentsMoreLayout = ({ children }) => ( +
+ {children} +
+); +registerComponent({ name: 'DatatableContentsMoreLayout', component: DatatableContentsMoreLayout }); +const DatatableLoadMoreButton = ({ count, totalCount, Components, children, ...otherProps }) => ( + {children} +); +registerComponent({ name: 'DatatableLoadMoreButton', component: DatatableLoadMoreButton }); + /* DatatableTitle Component */ const DatatableTitle = ({ title }) => -
{title}
+
{title}
; registerComponent('DatatableTitle', DatatableTitle); @@ -268,52 +384,81 @@ DatatableRow Component */ const DatatableRow = (props, { intl }) => { - const { collection, columns, document, showEdit, currentUser, options, editFormOptions, rowClass } = props; + const { collection, columns, document, showEdit, + currentUser, options, editFormOptions, rowClass, Components } = props; const canEdit = collection && collection.options && collection.options.mutations && collection.options.mutations.edit && collection.options.mutations.edit.check(currentUser, document); const row = typeof rowClass === 'function' ? rowClass(document) : rowClass || ''; const modalProps = { title: {document._id} }; + const sortedColumns = _sortBy(columns, column => column.order); return ( - - - {_.sortBy(columns, column => column.order).map((column, index) => )} - + + { + sortedColumns + .map((column, index) => ( + + ))} {showEdit && canEdit ? - + - + : null} - - - ) -} + + ); +}; +DatatableRow.propTypes = { + Components: PropTypes.object.isRequired +}; registerComponent('DatatableRow', DatatableRow); DatatableRow.contextTypes = { intl: intlShape }; +const DatatableRowLayout = ({ children, ...otherProps }) => ( + + {children} + +); +registerComponent({ name: 'DatatableRowLayout', component: DatatableRowLayout }); /* DatatableCell Component */ -const DatatableCell = ({ column, document, currentUser }) => { - const Component = column.component || column.componentName && Components[column.componentName] || Components.DatatableDefaultCell; - const columnName = column.name || column; +const DatatableCell = ({ column, document, currentUser, Components }) => { + const Component = column.component + || column.componentName && Components[column.componentName] + || Components.DatatableDefaultCell; + const columnName = getColumnName(column); return ( - - ) -} + + + + ); +}; +DatatableCell.propTypes = { + Components: PropTypes.object.isRequired +}; registerComponent('DatatableCell', DatatableCell); +const DatatableCellLayout = ({ children, ...otherProps }) => ( + {children} +); +registerComponent({ name: 'DatatableCellLayout', component: DatatableCellLayout }); + /* DatatableDefaultCell Component */ const DatatableDefaultCell = ({ column, document }) => -
{typeof column === 'string' ? getFieldValue(document[column]) : getFieldValue(document[column.name])}
+
{typeof column === 'string' ? getFieldValue(document[column]) : getFieldValue(document[column.name])}
; registerComponent('DatatableDefaultCell', DatatableDefaultCell); + diff --git a/packages/vulcan-core/lib/modules/components/DynamicLoading.jsx b/packages/vulcan-core/lib/modules/components/DynamicLoading.jsx index 7cfe32e6d..94ca176b2 100644 --- a/packages/vulcan-core/lib/modules/components/DynamicLoading.jsx +++ b/packages/vulcan-core/lib/modules/components/DynamicLoading.jsx @@ -11,7 +11,7 @@ const DynamicLoading = ({ isLoading, pastDelay, error }) => { } else { return null; } -} +}; registerComponent('DynamicLoading', DynamicLoading); diff --git a/packages/vulcan-core/lib/modules/components/EditButton.jsx b/packages/vulcan-core/lib/modules/components/EditButton.jsx index 026084f9e..4ad5be21e 100644 --- a/packages/vulcan-core/lib/modules/components/EditButton.jsx +++ b/packages/vulcan-core/lib/modules/components/EditButton.jsx @@ -32,15 +32,15 @@ EditForm Component const EditForm = ({ closeModal, successCallback, removeSuccessCallback, ...props }) => { const success = successCallback - ? () => { - successCallback(); + ? document => { + successCallback(document); closeModal(); } : closeModal; const remove = removeSuccessCallback - ? () => { - removeSuccessCallback(); + ? document => { + removeSuccessCallback(document); closeModal(); } : closeModal; diff --git a/packages/vulcan-core/lib/modules/components/Error404.jsx b/packages/vulcan-core/lib/modules/components/Error404.jsx index 2ac5c3a93..8e40cb26d 100644 --- a/packages/vulcan-core/lib/modules/components/Error404.jsx +++ b/packages/vulcan-core/lib/modules/components/Error404.jsx @@ -7,8 +7,8 @@ const Error404 = () => {

- ) -} + ); +}; Error404.displayName = 'Error404'; diff --git a/packages/vulcan-core/lib/modules/components/Flash.jsx b/packages/vulcan-core/lib/modules/components/Flash.jsx index 1d88ebee1..0f5fd8c5a 100644 --- a/packages/vulcan-core/lib/modules/components/Flash.jsx +++ b/packages/vulcan-core/lib/modules/components/Flash.jsx @@ -11,7 +11,7 @@ class Flash extends PureComponent { } componentDidMount() { - this.props.markAsSeen(this.props.message._id); + this.props.markAsSeen && this.props.markAsSeen(this.props.message._id); } dismissFlash(e) { @@ -42,25 +42,20 @@ class Flash extends PureComponent { }; render() { - const { message, type } = this.getProperties(); + + const { message, type = 'danger' } = this.getProperties(); const flashType = type === 'error' ? 'danger' : type; // if flashType is "error", use "danger" instead return ( - - {message} + + ); } } Flash.propTypes = { - message: PropTypes.object.isRequired, - markAsSeen: PropTypes.func.isRequired, - clear: PropTypes.func.isRequired + message: PropTypes.oneOfType([PropTypes.object.isRequired, PropTypes.string.isRequired]) }; Flash.contextTypes = { @@ -69,17 +64,12 @@ Flash.contextTypes = { registerComponent('Flash', Flash); -const FlashMessages = ({ messages, clear, markAsSeen }) => { +const FlashMessages = ({messages, clear, markAsSeen, className}) => { return ( -
- {messages.filter(message => message.show).map(message => ( - - ))} +
+ {messages + .filter(message => message.show) + .map(message => )}
); }; diff --git a/packages/vulcan-core/lib/modules/components/HeadTags.jsx b/packages/vulcan-core/lib/modules/components/HeadTags.jsx index b8a403e98..8f8662125 100644 --- a/packages/vulcan-core/lib/modules/components/HeadTags.jsx +++ b/packages/vulcan-core/lib/modules/components/HeadTags.jsx @@ -75,7 +75,7 @@ class HeadTags extends PureComponent { } else { HeadComponent = componentOrArray; } - return + return ; })}
diff --git a/packages/vulcan-core/lib/modules/components/HelloWorld.jsx b/packages/vulcan-core/lib/modules/components/HelloWorld.jsx index 11bae5633..7c2cf26e1 100644 --- a/packages/vulcan-core/lib/modules/components/HelloWorld.jsx +++ b/packages/vulcan-core/lib/modules/components/HelloWorld.jsx @@ -13,18 +13,18 @@ const wrapper = { display: 'flex', alignItems: 'center', justifyContent: 'center', -} +}; const header = { textAlign: 'center', -} +}; const code = { border: '1px solid #ccc', borderRadius: 3, padding: '10px 20px', background: 'white', -} +}; function escapeHtml(unsafe) { return unsafe @@ -73,7 +73,7 @@ addRoute({ name: 'home', path: '/', componentName: 'Home' });
- + ; HelloWorld.displayName = 'HelloWorld'; diff --git a/packages/vulcan-core/lib/modules/components/Icon.jsx b/packages/vulcan-core/lib/modules/components/Icon.jsx index aa03499a3..6ac4a6f63 100644 --- a/packages/vulcan-core/lib/modules/components/Icon.jsx +++ b/packages/vulcan-core/lib/modules/components/Icon.jsx @@ -7,7 +7,7 @@ const Icon = ({ name, iconClass, onClick }) => { iconClass = (typeof iconClass === 'string') ? ' '+iconClass : ''; const c = 'icon fa fa-fw fa-' + iconCode + ' icon-' + name + iconClass; return ; -} +}; Icon.displayName = 'Icon'; diff --git a/packages/vulcan-core/lib/modules/components/Layout.jsx b/packages/vulcan-core/lib/modules/components/Layout.jsx index e84e97471..7d83b6694 100644 --- a/packages/vulcan-core/lib/modules/components/Layout.jsx +++ b/packages/vulcan-core/lib/modules/components/Layout.jsx @@ -2,7 +2,7 @@ import { Components, registerComponent } from 'meteor/vulcan:lib'; import React from 'react'; const Layout = ({children}) => -
{children}
+
{children}
; Layout.displayName = 'Layout'; diff --git a/packages/vulcan-core/lib/modules/components/Loading.jsx b/packages/vulcan-core/lib/modules/components/Loading.jsx index a34da33e9..e2fd1dd40 100644 --- a/packages/vulcan-core/lib/modules/components/Loading.jsx +++ b/packages/vulcan-core/lib/modules/components/Loading.jsx @@ -8,8 +8,8 @@ const Loading = props => {
- ) -} + ); +}; Loading.displayName = 'Loading'; diff --git a/packages/vulcan-core/lib/modules/components/MutationButton.jsx b/packages/vulcan-core/lib/modules/components/MutationButton.jsx index a08bcb391..d5fe2a202 100644 --- a/packages/vulcan-core/lib/modules/components/MutationButton.jsx +++ b/packages/vulcan-core/lib/modules/components/MutationButton.jsx @@ -32,6 +32,7 @@ class MutationButtonInner extends PureComponent { successCallback(result); } }).catch(error => { + this.setState({ loading: false }); if(errorCallback) { errorCallback(error); } @@ -46,6 +47,8 @@ class MutationButtonInner extends PureComponent { delete rest[mutationName]; delete rest.mutationOptions; delete rest.mutationArguments; + delete rest.successCallback; + delete rest.errorCallback; return ; } @@ -80,6 +83,6 @@ const LoadingButton = ({ loading, label, onClick, children, ...rest }) => { ); -} +}; registerComponent('LoadingButton', LoadingButton); diff --git a/packages/vulcan-core/lib/modules/components/NewButton.jsx b/packages/vulcan-core/lib/modules/components/NewButton.jsx index 1451ff8bf..a7a3c88d5 100644 --- a/packages/vulcan-core/lib/modules/components/NewButton.jsx +++ b/packages/vulcan-core/lib/modules/components/NewButton.jsx @@ -31,8 +31,8 @@ NewForm Component const NewForm = ({ closeModal, successCallback, ...props }) => { const success = successCallback - ? () => { - successCallback(); + ? document => { + successCallback(document); closeModal(); } : closeModal; diff --git a/packages/vulcan-core/lib/modules/components/RouterHook.jsx b/packages/vulcan-core/lib/modules/components/RouterHook.jsx index 9b9254043..1998659f0 100644 --- a/packages/vulcan-core/lib/modules/components/RouterHook.jsx +++ b/packages/vulcan-core/lib/modules/components/RouterHook.jsx @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import { registerComponent, runCallbacks } from 'meteor/vulcan:lib'; +import { registerComponent, runCallbacks, runCallbacksAsync } from 'meteor/vulcan:lib'; import { withApollo } from 'react-apollo'; class RouterHook extends PureComponent { @@ -8,15 +8,17 @@ class RouterHook extends PureComponent { this.runOnUpdateCallback(props); } - componentWillReceiveProps(nextProps) { - this.runOnUpdateCallback(nextProps); + componentDidUpdate(nextProps) { + this.runOnUpdateCallback(this.props, nextProps); } - runOnUpdateCallback = props => { + runOnUpdateCallback = (props, nextProps = {}) => { const { currentRoute, client } = props; // the first argument is an item to iterate on, needed by vulcan:lib/callbacks // note: this item is not used in this specific callback: router.onUpdate runCallbacks('router.onUpdate', {}, currentRoute, client.store, client); + + runCallbacksAsync('router.onUpdate.async', props, nextProps); }; render() { diff --git a/packages/vulcan-core/lib/modules/components/Welcome.jsx b/packages/vulcan-core/lib/modules/components/Welcome.jsx index fc35053cd..b023d03b7 100644 --- a/packages/vulcan-core/lib/modules/components/Welcome.jsx +++ b/packages/vulcan-core/lib/modules/components/Welcome.jsx @@ -12,18 +12,18 @@ const wrapper = { display: 'flex', alignItems: 'center', justifyContent: 'center', -} +}; const header = { textAlign: 'center', -} +}; const code = { border: '1px solid #ccc', borderRadius: 3, padding: '10px 20px', background: 'white', -} +}; const Welcome = props =>
@@ -47,7 +47,7 @@ addRoute({ name: 'home', path: '/', componentName: 'HelloWorld' });
- + ; Welcome.displayName = 'Welcome'; diff --git a/packages/vulcan-core/lib/modules/containers/withAccess.js b/packages/vulcan-core/lib/modules/containers/withAccess.js index 113f1c720..a778e5f82 100644 --- a/packages/vulcan-core/lib/modules/containers/withAccess.js +++ b/packages/vulcan-core/lib/modules/containers/withAccess.js @@ -32,5 +32,5 @@ export default function withAccess (options) { AccessComponent.displayName = `withAccess(${WrappedComponent.displayName})`; return withRouter(withCurrentUser(AccessComponent)); - } + }; } diff --git a/packages/vulcan-core/lib/modules/containers/withComponents.js b/packages/vulcan-core/lib/modules/containers/withComponents.js new file mode 100644 index 000000000..3772b56a6 --- /dev/null +++ b/packages/vulcan-core/lib/modules/containers/withComponents.js @@ -0,0 +1,28 @@ +/** + * This HOC will load the global Components. + * If a "components" prop is passed, it will be merged with the global Components. + * + * This allow local replacement of global components, for example if + * you want a specific submit button but only for one specific form. + */ +import React from 'react'; +import PropTypes from 'prop-types'; +import { mergeWithComponents } from 'meteor/vulcan:lib'; + +const withComponents = C => { + const WrappedComponent = ({ components, formComponents, ...otherProps }) => { + //if (formComponents){ + // console.warn('"formComponents" prop is deprecated, use "components" prop instead (same behaviour)'); + //} + const Components = mergeWithComponents(components || formComponents); + return ; + }; + WrappedComponent.displayName = `withComponents(${C.displayName})`; + WrappedComponent.propTypes = { + formComponents: PropTypes.object, + components: PropTypes.object + }; + return WrappedComponent; +}; + +export default withComponents; \ No newline at end of file diff --git a/packages/vulcan-core/lib/modules/containers/withCreate.js b/packages/vulcan-core/lib/modules/containers/withCreate.js index f685ba426..4ce988088 100644 --- a/packages/vulcan-core/lib/modules/containers/withCreate.js +++ b/packages/vulcan-core/lib/modules/containers/withCreate.js @@ -30,7 +30,7 @@ import React, { Component } from 'react'; import { graphql } from 'react-apollo'; import gql from 'graphql-tag'; import { createClientTemplate } from 'meteor/vulcan:core'; -import { extractCollectionInfo, extractFragmentInfo } from './handleOptions'; +import { extractCollectionInfo, extractFragmentInfo } from 'meteor/vulcan:lib'; const withCreate = options => { const { collectionName, collection } = extractCollectionInfo(options); diff --git a/packages/vulcan-core/lib/modules/containers/withCurrentUser.js b/packages/vulcan-core/lib/modules/containers/withCurrentUser.js index 0de240093..30a9545f0 100644 --- a/packages/vulcan-core/lib/modules/containers/withCurrentUser.js +++ b/packages/vulcan-core/lib/modules/containers/withCurrentUser.js @@ -26,6 +26,6 @@ const withCurrentUser = component => { }, } )(component); -} +}; export default withCurrentUser; diff --git a/packages/vulcan-core/lib/modules/containers/withDelete.js b/packages/vulcan-core/lib/modules/containers/withDelete.js index fbf159c69..6954befcf 100644 --- a/packages/vulcan-core/lib/modules/containers/withDelete.js +++ b/packages/vulcan-core/lib/modules/containers/withDelete.js @@ -30,7 +30,7 @@ import React, { Component } from 'react'; import { graphql } from 'react-apollo'; import gql from 'graphql-tag'; import { deleteClientTemplate } from 'meteor/vulcan:core'; -import { extractCollectionInfo, extractFragmentInfo } from './handleOptions'; +import { extractCollectionInfo, extractFragmentInfo } from 'meteor/vulcan:lib'; const withDelete = options => { const { collectionName, collection } = extractCollectionInfo(options); diff --git a/packages/vulcan-core/lib/modules/containers/withMulti.js b/packages/vulcan-core/lib/modules/containers/withMulti.js index bb7ffebd8..9a7d49cf5 100644 --- a/packages/vulcan-core/lib/modules/containers/withMulti.js +++ b/packages/vulcan-core/lib/modules/containers/withMulti.js @@ -36,16 +36,16 @@ Terms object can have the following properties: import { withApollo, graphql } from 'react-apollo'; import gql from 'graphql-tag'; -import { getSetting, Utils, multiClientTemplate } from 'meteor/vulcan:lib'; +import update from 'immutability-helper'; +import { getSetting, Utils, multiClientTemplate, extractCollectionInfo, extractFragmentInfo } from 'meteor/vulcan:lib'; +import Mingo from 'mingo'; import compose from 'recompose/compose'; import withState from 'recompose/withState'; -import { extractCollectionInfo, extractFragmentInfo } from './handleOptions'; - export default function withMulti(options) { // console.log(options) - const { + let { limit = 10, pollInterval = getSetting('pollInterval', 20000), enableTotal = true, @@ -53,6 +53,10 @@ export default function withMulti(options) { extraQueries } = options; + // if this is the SSR process, set pollInterval to null + // see https://github.com/apollographql/apollo-client/issues/1704#issuecomment-322995855 + pollInterval = typeof window === 'undefined' ? null : pollInterval; + const { collectionName, collection } = extractCollectionInfo(options); const { fragmentName, fragment } = extractFragmentInfo(options, collectionName); @@ -156,8 +160,8 @@ export default function withMulti(options) { typeof providedTerms === 'undefined' ? { /*...props.ownProps.terms,*/ ...props.ownProps.paginationTerms, - limit: results.length + props.ownProps.paginationTerms.itemsPerPage - } + limit: results.length + props.ownProps.paginationTerms.itemsPerPage + } : providedTerms; props.ownProps.setPaginationTerms(newTerms); diff --git a/packages/vulcan-core/lib/modules/containers/withMutation.js b/packages/vulcan-core/lib/modules/containers/withMutation.js index 3b0d33aa9..a2ee2ef3c 100644 --- a/packages/vulcan-core/lib/modules/containers/withMutation.js +++ b/packages/vulcan-core/lib/modules/containers/withMutation.js @@ -23,7 +23,7 @@ export default function withMutation({name, args, fragmentName}) { fragment = getFragment(fragmentName); fragmentBlock = `{ ...${fragmentName} - }` + }`; } if (args) { @@ -33,13 +33,13 @@ export default function withMutation({name, args, fragmentName}) { mutation ${name}(${args1}) { ${name}(${args2})${fragmentBlock} } - ` + `; } else { mutation = ` mutation ${name} { ${name}${fragmentBlock} } - ` + `; } return graphql(gql`${mutation}${fragmentName ? fragment : ''}`, { diff --git a/packages/vulcan-core/lib/modules/containers/withSingle.js b/packages/vulcan-core/lib/modules/containers/withSingle.js index 29d60d583..721113582 100644 --- a/packages/vulcan-core/lib/modules/containers/withSingle.js +++ b/packages/vulcan-core/lib/modules/containers/withSingle.js @@ -2,12 +2,15 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { graphql } from 'react-apollo'; import gql from 'graphql-tag'; -import { getSetting, singleClientTemplate, Utils } from 'meteor/vulcan:lib'; - -import { extractCollectionInfo, extractFragmentInfo } from './handleOptions'; +import { getSetting, singleClientTemplate, Utils, extractCollectionInfo, extractFragmentInfo } from 'meteor/vulcan:lib'; export default function withSingle(options) { - const { pollInterval = getSetting('pollInterval', 20000), enableCache = false, extraQueries } = options; + + let { pollInterval = getSetting('pollInterval', 20000), enableCache = false, extraQueries } = options; + + // if this is the SSR process, set pollInterval to null + // see https://github.com/apollographql/apollo-client/issues/1704#issuecomment-322995855 + pollInterval = typeof window === 'undefined' ? null : pollInterval; const { collectionName, collection } = extractCollectionInfo(options); const { fragmentName, fragment } = extractFragmentInfo(options, collectionName); @@ -46,6 +49,7 @@ export default function withSingle(options) { const propertyName = options.propertyName || 'document'; const props = { loading: data.loading, + refetch: data.refetch, // document: Utils.convertDates(collection, data[singleResolverName]), [propertyName]: data[resolverName] && data[resolverName].result, fragmentName, diff --git a/packages/vulcan-core/lib/modules/containers/withSiteData.js b/packages/vulcan-core/lib/modules/containers/withSiteData.js new file mode 100644 index 000000000..83535b13e --- /dev/null +++ b/packages/vulcan-core/lib/modules/containers/withSiteData.js @@ -0,0 +1,32 @@ +import React, { Component } from 'react'; +import { graphql } from 'react-apollo'; +import gql from 'graphql-tag'; + +const withSiteData = component => { + + return graphql( + gql` + query getSiteData { + SiteData { + url + title + sourceVersion + logoUrl + } + } + `, { + alias: 'withSiteData', + + props(props) { + const { data } = props; + return { + siteDataLoading: data.loading, + siteData: data.SiteData, + siteDataData: data, + }; + }, + } + )(component); +}; + +export default withSiteData; diff --git a/packages/vulcan-core/lib/modules/containers/withUpdate.js b/packages/vulcan-core/lib/modules/containers/withUpdate.js index ec3a914bb..6ef5f9543 100644 --- a/packages/vulcan-core/lib/modules/containers/withUpdate.js +++ b/packages/vulcan-core/lib/modules/containers/withUpdate.js @@ -30,11 +30,9 @@ Child Props: import React, { Component } from 'react'; import { graphql } from 'react-apollo'; import gql from 'graphql-tag'; -import { updateClientTemplate } from 'meteor/vulcan:lib'; +import { updateClientTemplate, extractCollectionInfo, extractFragmentInfo } from 'meteor/vulcan:lib'; import clone from 'lodash/clone'; -import { extractCollectionInfo, extractFragmentInfo } from './handleOptions'; - const withUpdate = options => { const { collectionName, collection } = extractCollectionInfo(options); const { fragmentName, fragment } = extractFragmentInfo(options, collectionName); diff --git a/packages/vulcan-core/lib/modules/containers/withUpsert.js b/packages/vulcan-core/lib/modules/containers/withUpsert.js index 16a8b7b69..72e191aa9 100644 --- a/packages/vulcan-core/lib/modules/containers/withUpsert.js +++ b/packages/vulcan-core/lib/modules/containers/withUpsert.js @@ -33,7 +33,7 @@ import gql from 'graphql-tag'; import { upsertClientTemplate } from 'meteor/vulcan:core'; import clone from 'lodash/clone'; -import { extractCollectionInfo, extractFragmentInfo } from './handleOptions'; +import { extractCollectionInfo, extractFragmentInfo } from 'meteor/vulcan:lib'; const withUpsert = options => { const { collectionName, collection } = extractCollectionInfo(options); diff --git a/packages/vulcan-core/lib/modules/default_mutations.js b/packages/vulcan-core/lib/modules/default_mutations.js index 25784802a..92e48a53d 100644 --- a/packages/vulcan-core/lib/modules/default_mutations.js +++ b/packages/vulcan-core/lib/modules/default_mutations.js @@ -73,7 +73,7 @@ export function getDefaultMutations(options) { const collection = context[collectionName]; // check if current user can pass check function; else throw error - Utils.performCheck(this.check, context.currentUser, data); + Utils.performCheck(this.check, context.currentUser, data, '', `${typeName}.create`, collectionName); // pass document to boilerplate newMutator function return await createMutator({ @@ -167,7 +167,7 @@ export function getDefaultMutations(options) { } // check if user can perform operation; if not throw error - Utils.performCheck(this.check, context.currentUser, document); + Utils.performCheck(this.check, context.currentUser, document, document._id, `${typeName}.update`, collectionName); // call editMutator boilerplate function return await updateMutator({ @@ -282,7 +282,7 @@ export function getDefaultMutations(options) { throw new Error(`Could not find document to delete for selector: ${JSON.stringify(selector)}`); } - Utils.performCheck(this.check, context.currentUser, document, context); + Utils.performCheck(this.check, context.currentUser, document, context, document._id, `${typeName}.delete`, collectionName); return await deleteMutator({ collection, diff --git a/packages/vulcan-core/lib/modules/default_resolvers.js b/packages/vulcan-core/lib/modules/default_resolvers.js index 2090a1aca..6b44b3cb7 100644 --- a/packages/vulcan-core/lib/modules/default_resolvers.js +++ b/packages/vulcan-core/lib/modules/default_resolvers.js @@ -4,8 +4,7 @@ Default list, single, and total resolvers */ -import { Utils, debug, debugGroup, debugGroupEnd, Connectors, getTypeName, getCollectionName } from 'meteor/vulcan:lib'; -import { createError } from 'apollo-errors'; +import { Utils, debug, debugGroup, debugGroupEnd, Connectors, getTypeName, getCollectionName, throwError } from 'meteor/vulcan:lib'; const defaultOptions = { cacheMaxAge: 300 @@ -117,15 +116,22 @@ export function getDefaultResolvers(options) { if (allowNull) { return { result: null }; } else { - const MissingDocumentError = createError('app.missing_document', { message: 'app.missing_document' }); - throw new MissingDocumentError({ data: { documentId, selector } }); + throwError({ id: 'app.missing_document', data: {documentId, selector} }); } } // if collection has a checkAccess function defined, use it to perform a check on the current document // (will throw an error if check doesn't pass) if (collection.checkAccess) { - Utils.performCheck(collection.checkAccess, currentUser, doc, collection, documentId); + Utils.performCheck( + collection.checkAccess, + currentUser, + doc, + collection, + documentId, + `${typeName}.read.single`, + collectionName + ); } const restrictedDoc = Users.restrictViewableFields(currentUser, collection, doc); diff --git a/packages/vulcan-core/lib/modules/index.js b/packages/vulcan-core/lib/modules/index.js index f19003fe9..abc5165a6 100644 --- a/packages/vulcan-core/lib/modules/index.js +++ b/packages/vulcan-core/lib/modules/index.js @@ -36,6 +36,7 @@ export { default as withCurrentUser } from './containers/withCurrentUser.js'; export { default as withMutation } from './containers/withMutation.js'; export { default as withUpsert } from './containers/withUpsert.js'; +export { default as withComponents } from './containers/withComponents'; // OpenCRUD backwards compatibility export { default as withNew } from './containers/withCreate.js'; diff --git a/packages/vulcan-core/package.js b/packages/vulcan-core/package.js index 111c975b1..30e405707 100644 --- a/packages/vulcan-core/package.js +++ b/packages/vulcan-core/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:core', summary: 'Vulcan core package', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -9,19 +9,20 @@ Package.onUse(function (api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:lib@1.12.8', - 'vulcan:i18n@1.12.8', - 'vulcan:users@1.12.8', - 'vulcan:debug@1.12.8' + 'vulcan:lib@1.12.13', + 'vulcan:i18n@1.12.13', + 'vulcan:users@1.12.13', + 'vulcan:routing@1.12.13', + 'vulcan:debug@1.12.13' ]); - api.imply(['vulcan:lib@1.12.8']); + api.imply(['vulcan:lib@1.12.13']); api.mainModule('lib/server/main.js', 'server'); api.mainModule('lib/client/main.js', 'client'); }); Package.onTest(function (api) { - api.use(['ecmascript', 'meteortesting:mocha', 'vulcan:core']); + api.use(['ecmascript', 'meteortesting:mocha', 'vulcan:test', 'vulcan:core']); api.mainModule('./test/index.js'); }); diff --git a/packages/vulcan-core/test/components.test.js b/packages/vulcan-core/test/components.test.js new file mode 100644 index 000000000..5191eaab7 --- /dev/null +++ b/packages/vulcan-core/test/components.test.js @@ -0,0 +1,74 @@ +// setup JSDOM server side for testing (necessary for Enzyme to mount) +import 'jsdom-global/register'; +import React from 'react'; +import expect from 'expect'; +import { mount, shallow } from 'enzyme'; +import { Components } from 'meteor/vulcan:core'; +import { initComponentTest } from 'meteor/vulcan:test'; + + +// we must import all the other components, so that "registerComponent" is called +import '../lib/modules'; +import Datatable from '../lib/modules/components/Datatable'; +// stub collection +import { createCollection, getDefaultResolvers, getDefaultMutations, registerFragment } from 'meteor/vulcan:core'; +const createDummyCollection = (typeName, schema) => { + return createCollection({ + collectionName: typeName + 's', + typeName, + schema, + resolvers: getDefaultResolvers(typeName + 's'), + mutations: getDefaultMutations(typeName + 's') + }); +}; +const Articles = createDummyCollection('Article', { + name: { + type: String + } +}); +registerFragment(` + fragment ArticlesDefaultFragment on Article { + name + } +`); + +// setup Vulcan (load components, initialize fragments) +initComponentTest(); + + +describe('vulcan-core/components', function () { + describe('DataTable', function () { + it('shallow renders DataTable', function () { + const wrapper = shallow(); + expect(wrapper).toBeDefined(); + }); + it('render a static version', function () { + const wrapper = shallow(); + const content = wrapper.find('DatatableContents').first(); + expect(content).toBeDefined(); + }); + const context = { + intl: { + formatMessage: () => { }, + } + }; + it('mounts a static version', function () { + const wrapper = mount( + + , { + context, + childContextTypes: context + }); + expect(wrapper).toBeDefined(); + //const content = wrapper.find('DatatableContents').first(); + //expect(content).toBeDefined(); + }); + }); +}); \ No newline at end of file diff --git a/packages/vulcan-core/test/containers.test.js b/packages/vulcan-core/test/containers.test.js index c5c2dc386..2d7777523 100644 --- a/packages/vulcan-core/test/containers.test.js +++ b/packages/vulcan-core/test/containers.test.js @@ -1,18 +1,45 @@ -import { extractCollectionInfo, extractFragmentInfo } from '../lib/modules/containers/handleOptions'; +// setup JSDOM server side for testing (necessary for Enzyme to mount) +import 'jsdom-global/register'; +import React from 'react'; import expect from 'expect'; +import { shallow } from 'enzyme'; +import { Components } from 'meteor/vulcan:core'; +import { initComponentTest } from 'meteor/vulcan:test'; +import { withComponents } from '../lib/modules'; -describe('vulcan-core/containers', function() { - describe('handleOptions', function() { + +// we must import all the other components, so that "registerComponent" is called +import '../lib/modules'; +// setup Vulcan (load components, initialize fragments) +initComponentTest(); + + +describe('vulcan-core/containers', function () { + describe('withComponents', function () { + it('should override components', function () { + // replace any component for testing purpose + const firstComponentName = Components[Object.keys(Components)[0]]; + const FooComponent = () => 'FOO'; + const components = { [firstComponentName]: FooComponent }; + const MyComponent = withComponents(({ Components }) => Components[firstComponentName]()); + const wrapper = shallow(); + expect(wrapper.prop('Components')).toBeDefined(); + expect(wrapper.prop('Components')[firstComponentName]).toEqual(FooComponent); + expect(wrapper.html()).toEqual('FOO'); + + }); + }); + describe('handleOptions', function () { const expectedCollectionName = 'COLLECTION_NAME'; const collectionNameOptions = { collectionName: expectedCollectionName }; const expectedCollection = { options: collectionNameOptions }; - it('get collectionName from collection', function() { + it('get collectionName from collection', function () { const options = { collection: expectedCollection }; const { collection, collectionName } = extractCollectionInfo(options); expect(collection).toEqual(expectedCollection); expect(collectionName).toEqual(expectedCollectionName); }); - it('get collection from collectioName', function() { + it('get collection from collectioName', function () { // MOCK getCollection const { collection, collectionName } = extractCollectionInfo(collectionNameOptions); expect(collection).toEqual(expectedCollection); @@ -20,20 +47,20 @@ describe('vulcan-core/containers', function() { }); const expectedFragmentName = 'FRAGMENT_NAME'; const expectedFragment = { definitions: [{ name: { value: expectedFragmentName } }] }; - it('get fragment from fragmentName', function() { + it('get fragment from fragmentName', function () { // MOCK getCollection const options = { fragmentName: expectedFragmentName }; const { fragment, fragmentName } = extractFragmentInfo(options); expect(fragment).toEqual(expectedFragment); expect(fragmentName).toEqual(expectedFragmentName); }); - it('get fragmentName from fragment', function() { + it('get fragmentName from fragment', function () { const options = { fragment: expectedFragment }; const { fragment, fragmentName } = extractFragmentInfo(options); expect(fragment).toEqual(expectedFragment); expect(fragmentName).toEqual(expectedFragmentName); }); - it('get fragmentName and fragment from collectionName', function() { + it('get fragmentName and fragment from collectionName', function () { // if options does not contain fragment, we get the collection default fragment based on its name const options = {}; const { fragment, fragmentName } = extractFragmentInfo(options, expectedCollectionName); @@ -42,13 +69,13 @@ describe('vulcan-core/containers', function() { }); }); - describe('withMessages', function() { + describe('withMessages', function () { const WrappedComponent = props =>
; const apolloClient = null; // TODO: init an apolloClient, that must be available in the context - it.skip('pass messages', function() {}); - it.skip('add a flash message', function() {}); - it.skip('mark a flash message as seen', function() {}); - it.skip('hide a flash message as seen', function() {}); - it.skip('clear seen', function() {}); + it.skip('pass messages', function () { }); + it.skip('add a flash message', function () { }); + it.skip('mark a flash message as seen', function () { }); + it.skip('hide a flash message as seen', function () { }); + it.skip('clear seen', function () { }); }); }); diff --git a/packages/vulcan-core/test/index.js b/packages/vulcan-core/test/index.js index 37dc71c42..1fe7eec6d 100644 --- a/packages/vulcan-core/test/index.js +++ b/packages/vulcan-core/test/index.js @@ -1 +1,3 @@ -import './containers.test.js'; +import './resolvers.test'; +import './components.test'; +import './containers.test'; diff --git a/packages/vulcan-core/test/resolvers.test.js b/packages/vulcan-core/test/resolvers.test.js new file mode 100644 index 000000000..0cf667a70 --- /dev/null +++ b/packages/vulcan-core/test/resolvers.test.js @@ -0,0 +1,84 @@ +import expect from 'expect'; +import { getDefaultResolvers } from '../lib/modules/default_resolvers'; + +describe('vulcan:core/default_resolvers', function() { + const resolversOptions = { + typeName: 'Dummy', + collectionName: 'Dummies', + options: {} + }; + describe('single', function() { + it('defines the correct fields', function() { + const { single } = getDefaultResolvers(resolversOptions); + const { description, resolver } = single; + expect(description).toBeDefined(); + expect(resolver).toBeDefined(); + expect(resolver).toBeInstanceOf(Function); + }); + const buildContext = ({ load = () => null, currentUser = null }) => ({ + Dummies: { + options: { collectionName: 'Dummies' }, + loader: { load } + //findOne() { + // console.log('FINDE_ONE'); + //} + }, //TODO fake collection + Users: { + restrictViewableFields: (currentUser, collection, doc) => doc + }, + currentUser + }); + // TODO: what's the name of this argument? handles cache + const lastArg = { cacheControl: {} }; + // eslint-disable-next-line no-unused-vars + const loggedInUser = { _id: 'foobar', groups: [], isAdmin: false }; + // eslint-disable-next-line no-unused-vars + const adminUser = { _id: 'foobar', groups: [], isAdmin: true }; + const getSingleResolver = () => getDefaultResolvers(resolversOptions).single.resolver; + + // TODO: the current behaviour is not consistent, could be improved + // @see https://github.com/VulcanJS/Vulcan/issues/2118 + it.skip('return null if documentId is undefined', function() { + const resolver = getSingleResolver(); + // no documentId + const input = { selector: {} }; + // non empty db + const context = buildContext({ load: () => ({ _id: 'my-document' }) }); + const res = resolver(null, { input }, context, lastArg); + return expect(res).resolves.toEqual({ result: null }); + }); + it('return document in case of success', function() { + const resolver = getSingleResolver(); + const documentId = 'my-document'; + const document = { _id: documentId }; + const input = { selector: { documentId } }; + // non empty db + const context = buildContext({ + load: () => { + return document; + } + }); + const res = resolver(null, { input }, context, lastArg); + return expect(res).resolves.toEqual({ result: document }); + }); + it('return null if failure to find doc but allowNull is true', function() { + const resolver = getSingleResolver(); + const documentId = 'bad-document'; + const input = { selector: { documentId }, allowNull: true }; + // empty db + const context = buildContext({ load: () => null }); + const res = resolver(null, { input }, context, lastArg); + return expect(res).resolves.toEqual({ result: null }); + }); + it('throws if documentId is defined but does not match any document', function() { + const resolver = getSingleResolver(); + const documentId = 'bad-document'; + // eslint-disable-next-line no-unused-vars + const document = { _id: documentId }; + const input = { selector: { documentId } }; + // empty db + const context = buildContext({ load: () => null }); + return expect(resolver(null, { input }, context, lastArg)).rejects.toThrow(); + }); + }); +}); diff --git a/packages/vulcan-debug/lib/components/AdminLayout.jsx b/packages/vulcan-debug/lib/components/AdminLayout.jsx index 1cc1739eb..238b7dfc4 100644 --- a/packages/vulcan-debug/lib/components/AdminLayout.jsx +++ b/packages/vulcan-debug/lib/components/AdminLayout.jsx @@ -3,8 +3,8 @@ import { Components, registerComponent } from 'meteor/vulcan:lib'; const adminStyles = { padding: '20px' -} +}; -const AdminLayout = props =>
{props.children}
+const AdminLayout = props =>
{props.children}
; registerComponent('AdminLayout', AdminLayout); \ No newline at end of file diff --git a/packages/vulcan-debug/lib/components/Callbacks.jsx b/packages/vulcan-debug/lib/components/Callbacks.jsx index 7a3a8c89b..46e4277b9 100644 --- a/packages/vulcan-debug/lib/components/Callbacks.jsx +++ b/packages/vulcan-debug/lib/components/Callbacks.jsx @@ -3,7 +3,7 @@ import { registerComponent, Components } from 'meteor/vulcan:lib'; import Callbacks from '../modules/callbacks/collection.js'; const CallbacksName = ({ document }) => - {document.name} + {document.name}; const CallbacksDashboard = props =>
@@ -23,7 +23,7 @@ const CallbacksDashboard = props => 'hooks', ]} /> -
+
; registerComponent('Callbacks', CallbacksDashboard); diff --git a/packages/vulcan-debug/lib/components/Dashboard.jsx b/packages/vulcan-debug/lib/components/Dashboard.jsx new file mode 100644 index 000000000..4c58df0c2 --- /dev/null +++ b/packages/vulcan-debug/lib/components/Dashboard.jsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { registerComponent } from 'meteor/vulcan:lib'; +import { Link } from 'react-router'; + +function Dashboard() { + return ( +
+

Debug Dashboard

+
    +
  • + Callbacks +
  • +
  • + Components +
  • +
  • + Emails +
  • +
  • + Groups +
  • +
  • + I18n +
  • +
  • + Routes +
  • +
  • + Settings +
  • +
+
+ ); +} + +registerComponent({ name: 'DebugDashboard', component: Dashboard, hocs: [] }); diff --git a/packages/vulcan-debug/lib/components/Emails.jsx b/packages/vulcan-debug/lib/components/Emails.jsx index 478a0e63b..fdd9eda29 100644 --- a/packages/vulcan-debug/lib/components/Emails.jsx +++ b/packages/vulcan-debug/lib/components/Emails.jsx @@ -10,7 +10,7 @@ class Email extends PureComponent { this.sendTest = this.sendTest.bind(this); this.state = { loading: false - } + }; } sendTest() { @@ -44,7 +44,7 @@ class Email extends PureComponent { - ) + ); } } @@ -81,7 +81,7 @@ const Emails = (/* props*/) => { - ) + ); }; registerComponent('Emails', Emails); diff --git a/packages/vulcan-debug/lib/components/Groups.jsx b/packages/vulcan-debug/lib/components/Groups.jsx index 4ef485e10..f16a6e161 100644 --- a/packages/vulcan-debug/lib/components/Groups.jsx +++ b/packages/vulcan-debug/lib/components/Groups.jsx @@ -8,8 +8,8 @@ const Group = ({name, actions}) => { {name}
    {actions.map((action, index) =>
  • {action}
  • )}
- ) -} + ); +}; const Groups = props => { return ( @@ -33,8 +33,8 @@ const Groups = props => { - ) -} + ); +}; registerComponent('Groups', Groups); diff --git a/packages/vulcan-debug/lib/components/I18n.jsx b/packages/vulcan-debug/lib/components/I18n.jsx new file mode 100644 index 000000000..42499d8f5 --- /dev/null +++ b/packages/vulcan-debug/lib/components/I18n.jsx @@ -0,0 +1,70 @@ +import React from 'react'; +import { registerComponent, Components, Strings, Locales } from 'meteor/vulcan:lib'; +import PropTypes from 'prop-types'; +import sortedUniq from 'lodash/sortedUniq'; + +/** + * Internationalization debugging page + * + * + **/ +function LocaleSwitcher(props, context) { + return ( +
+ Switch locales : + {Locales.map(localeObj => ( + context.setLocale(localeObj.id)}> + {localeObj.label} + + ))} +
+ ); +} +LocaleSwitcher.contextTypes = { + getLocale: PropTypes.func, + setLocale: PropTypes.func, +}; + +export const I18n = (props, context) => { + // translations holds all the translations ids + let translations = []; + let columns = [ + { + name: 'id', + component: function({ document }) { + return document; + }, + }, + ]; + + // reunite all the ids in a single array (translations) and create the columns for each language + Object.keys(Strings).forEach(language => { + translations.push(...Object.keys(Strings[language])); + columns.push({ + name: language, + component: function({ document }) { + return Strings[language][document] || null; + }, + }); + }); + + //sort the array + translations.sort(); + //remove duplicates + let translationsUniq = sortedUniq(translations); + + return ( +
+

{'Your current locale: ' + context.getLocale()}

+ + +
+ ); +}; + +I18n.contextTypes = { + getLocale: PropTypes.func, + setLocale: PropTypes.func, +}; + +registerComponent({ name: 'I18n', component: I18n, hocs: [] }); diff --git a/packages/vulcan-debug/lib/components/Routes.jsx b/packages/vulcan-debug/lib/components/Routes.jsx index 251a18434..59cb2d98e 100644 --- a/packages/vulcan-debug/lib/components/Routes.jsx +++ b/packages/vulcan-debug/lib/components/Routes.jsx @@ -3,7 +3,7 @@ import { registerComponent, Components, Routes } from 'meteor/vulcan:lib'; import { Link } from 'react-router-dom'; const RoutePath = ({ document }) => - {document.path} + {document.path}; const RoutesDashboard = props =>
@@ -21,6 +21,6 @@ const RoutesDashboard = props => 'componentName', ]} /> -
+ ; registerComponent('Routes', RoutesDashboard); \ No newline at end of file diff --git a/packages/vulcan-debug/lib/components/Settings.jsx b/packages/vulcan-debug/lib/components/Settings.jsx index 57abb0ae6..8fb39dce7 100644 --- a/packages/vulcan-debug/lib/components/Settings.jsx +++ b/packages/vulcan-debug/lib/components/Settings.jsx @@ -3,7 +3,7 @@ import { registerComponent, Components } from 'meteor/vulcan:lib'; import Settings from '../modules/settings/collection.js'; const SettingName = ({ document }) => - {document.name} + {document.name}; const SettingsDashboard = props =>
@@ -20,7 +20,7 @@ const SettingsDashboard = props => 'serverOnly' ]} /> -
+ ; registerComponent('Settings', SettingsDashboard); diff --git a/packages/vulcan-debug/lib/modules/components.js b/packages/vulcan-debug/lib/modules/components.js index 214b98b06..0dcc47be3 100644 --- a/packages/vulcan-debug/lib/modules/components.js +++ b/packages/vulcan-debug/lib/modules/components.js @@ -6,3 +6,5 @@ import '../components/Settings.jsx'; import '../components/Callbacks.jsx'; import '../components/Routes.jsx'; import '../components/Components.jsx'; +import '../components/I18n.jsx'; +import '../components/Dashboard.jsx'; diff --git a/packages/vulcan-debug/lib/modules/routes.js b/packages/vulcan-debug/lib/modules/routes.js index 055029b61..c4e7957af 100644 --- a/packages/vulcan-debug/lib/modules/routes.js +++ b/packages/vulcan-debug/lib/modules/routes.js @@ -2,11 +2,13 @@ import { addRoute, getDynamicComponent } from 'meteor/vulcan:lib'; addRoute([ // {name: 'cheatsheet', path: '/cheatsheet', component: import('./components/Cheatsheet.jsx')}, - {name: 'groups', path: '/groups', component: () => getDynamicComponent(import('../components/Groups.jsx')), layoutName: 'AdminLayout'}, - {name: 'settings', path: '/settings', componentName: 'Settings', layoutName: 'AdminLayout'}, - {name: 'callbacks', path: '/callbacks', componentName: 'Callbacks', layoutName: 'AdminLayout'}, + { name: 'debug', path: '/debug', componentName: 'DebugDashboard', layoutName: 'AdminLayout' }, + { name: 'debugGroups', path: '/debug/groups', component: () => getDynamicComponent(import('../components/Groups.jsx')), layoutName: 'AdminLayout' }, + { name: 'debugSettings', path: '/debug/settings', componentName: 'Settings', layoutName: 'AdminLayout' }, + { name: 'debugCallbacks', path: '/debug/callbacks', componentName: 'Callbacks', layoutName: 'AdminLayout' }, // {name: 'emails', path: '/emails', component: () => getDynamicComponent(import('./components/Emails.jsx'))}, - {name: 'emails', path: '/emails', componentName: 'Emails', layoutName: 'AdminLayout'}, - {name: 'routes', path: '/routes', componentName: 'Routes', layoutName: 'AdminLayout'}, - {name: 'components', path: '/components', componentName: 'Components', layoutName: 'AdminLayout'}, -]); \ No newline at end of file + { name: 'debugEmails', path: '/debug/emails', componentName: 'Emails', layoutName: 'AdminLayout' }, + { name: 'debugRoutes', path: '/debug/routes', componentName: 'Routes', layoutName: 'AdminLayout' }, + { name: 'debugComponents', path: '/debug/components', componentName: 'Components', layoutName: 'AdminLayout' }, + { name: 'debugI18n', path: '/debug/i18n', componentName: 'I18n', layoutName: 'AdminLayout' }, +]); diff --git a/packages/vulcan-debug/package.js b/packages/vulcan-debug/package.js index 53e4a4236..641bbea27 100644 --- a/packages/vulcan-debug/package.js +++ b/packages/vulcan-debug/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:debug', summary: 'Vulcan debug package', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git', debugOnly: true }); @@ -12,13 +12,13 @@ Package.onUse(function (api) { api.use([ - 'fourseven:scss@4.5.0', + 'fourseven:scss@4.10.0', 'dynamic-import@0.1.1', // Vulcan packages - 'vulcan:lib@1.12.8', - 'vulcan:email@1.12.8', + 'vulcan:lib@1.12.13', + 'vulcan:email@1.12.13', ]); diff --git a/packages/vulcan-email/lib/server/email.js b/packages/vulcan-email/lib/server/email.js index 2b760e9b9..61d26d2af 100644 --- a/packages/vulcan-email/lib/server/email.js +++ b/packages/vulcan-email/lib/server/email.js @@ -3,7 +3,27 @@ import VulcanEmail from '../namespace.js'; import Juice from 'juice'; import htmlToText from 'html-to-text'; import Handlebars from 'handlebars'; -import { Utils, getSetting, registerSetting, runQuery, Strings } from 'meteor/vulcan:lib'; // import from vulcan:lib because vulcan:core is not loaded yet +import { Utils, getSetting, registerSetting, runQuery, Strings, getString } from 'meteor/vulcan:lib'; // import from vulcan:lib because vulcan:core is not loaded yet + +/* + +Get intl string. Usage: {{__ "posts.create"}} + +*/ +Handlebars.registerHelper('__', function(id, context) { + const s = getString({ id, locale: context.data.root.locale }); + return new Handlebars.SafeString(s); +}); + +/* + +Get intl string, accepts a second variables argument. Usage: {{__ "posts.create" postVariables}} + +*/ +Handlebars.registerHelper('___', function(id, variables, context) { + const s = getString({ id, variables, locale: context.data.root.locale }); + return new Handlebars.SafeString(s); +}); registerSetting('secondaryColor', '#444444'); registerSetting('accentColor', '#DD3416'); @@ -23,8 +43,12 @@ VulcanEmail.addTemplates = templates => { _.extend(VulcanEmail.templates, templates); }; -VulcanEmail.getTemplate = templateName => - Handlebars.compile(VulcanEmail.templates[templateName], { noEscape: true, strict: true }); +VulcanEmail.getTemplate = templateName => { + if (!VulcanEmail.templates[templateName]) { + throw new Error(`Couldn't find email template named “${templateName}”`); + } + return Handlebars.compile(VulcanEmail.templates[templateName], { noEscape: true, strict: true }); +}; VulcanEmail.buildTemplate = (htmlContent, data = {}, locale) => { const emailProperties = { @@ -58,18 +82,18 @@ VulcanEmail.generateTextVersion = html => { }); }; -VulcanEmail.send = (to, subject, html, text, throwErrors, cc, bcc, replyTo) => { +VulcanEmail.send = (to, subject, html, text, throwErrors, cc, bcc, replyTo, headers) => { // TODO: limit who can send emails // TODO: fix this error: Error: getaddrinfo ENOTFOUND if (typeof to === 'object') { // eslint-disable-next-line no-redeclare - var { to, cc, bcc, replyTo, subject, html, text, throwErrors } = to; + var { to, cc, bcc, replyTo, subject, html, text, throwErrors, headers } = to; } const from = getSetting('defaultEmail', 'noreply@example.com'); const siteName = getSetting('title', 'Vulcan'); - subject = '[' + siteName + '] ' + subject; + subject = subject || '[' + siteName + ']'; if (typeof text === 'undefined') { // Auto-generate text version if it doesn't exist. Has bugs, but should be good enough. @@ -83,19 +107,22 @@ VulcanEmail.send = (to, subject, html, text, throwErrors, cc, bcc, replyTo) => { bcc: bcc, replyTo: replyTo, subject: subject, + headers: headers, text: text, html: html, }; - if (process.env.NODE_ENV === 'production' || getSetting('enableDevelopmentEmails', false)) { - console.log('//////// sending email…'); // eslint-disable-line - console.log('from: ' + from); // eslint-disable-line - console.log('cc: ' + cc); // eslint-disable-line - console.log('bcc: ' + bcc); // eslint-disable-line - console.log('replyTo: ' + replyTo); // eslint-disable-line - // console.log('html: '+html); - // console.log('text: '+text); + const shouldSendEmail = process.env.NODE_ENV === 'production' || getSetting('enableDevelopmentEmails', false); + console.log(`//////// sending email${shouldSendEmail ? '' : ' (simulation)'}…`); // eslint-disable-line + console.log('from: ' + from); // eslint-disable-line + console.log('to: ' + to); // eslint-disable-line + console.log('cc: ' + cc); // eslint-disable-line + console.log('bcc: ' + bcc); // eslint-disable-line + console.log('replyTo: ' + replyTo); // eslint-disable-line + console.log('headers: ' + JSON.stringify(headers)); // eslint-disable-line + + if (shouldSendEmail) { try { Email.send(email); } catch (error) { @@ -103,13 +130,6 @@ VulcanEmail.send = (to, subject, html, text, throwErrors, cc, bcc, replyTo) => { console.log(error); // eslint-disable-line if (throwErrors) throw error; } - } else { - console.log('//////// sending email (simulation)…'); // eslint-disable-line - console.log('from: ' + from); // eslint-disable-line - console.log('to: ' + to); // eslint-disable-line - console.log('cc: ' + cc); // eslint-disable-line - console.log('bcc: ' + bcc); // eslint-disable-line - console.log('replyTo: ' + replyTo); // eslint-disable-line } return email; @@ -121,20 +141,21 @@ VulcanEmail.build = async ({ emailName, variables, locale }) => { const result = email.query ? await runQuery(email.query, variables, { locale }) : { data: {} }; // if email has a data() function, merge its return value with results from the query - const data = email.data ? { ...result.data, ...email.data(variables) } : result.data; + const data = email.data ? { ...result.data, ...email.data({ data: result.data, variables, locale }) } : result.data; - const subject = typeof email.subject === 'function' ? email.subject(data) : email.subject; + const subject = typeof email.subject === 'function' ? email.subject({ data, variables, locale }) : email.subject; data.__ = Strings[locale]; + data.locale = locale; const html = VulcanEmail.buildTemplate(VulcanEmail.getTemplate(email.template)(data), data, locale); return { data, subject, html }; }; -VulcanEmail.buildAndSend = async ({ to, cc, bcc, replyTo, emailName, variables, locale = getSetting('locale') }) => { +VulcanEmail.buildAndSend = async ({ to, cc, bcc, replyTo, emailName, variables, locale = getSetting('locale'), headers }) => { const email = await VulcanEmail.build({ to, emailName, variables, locale }); - return VulcanEmail.send({ to, cc, bcc, replyTo, subject: email.subject, html: email.html }); + return VulcanEmail.send({ to, cc, bcc, replyTo, subject: email.subject, html: email.html, headers }); }; VulcanEmail.buildAndSendHTML = (to, subject, html) => VulcanEmail.send(to, subject, VulcanEmail.buildTemplate(html)); diff --git a/packages/vulcan-email/lib/server/routes.js b/packages/vulcan-email/lib/server/routes.js index ea0e9a46a..4d52257b2 100644 --- a/packages/vulcan-email/lib/server/routes.js +++ b/packages/vulcan-email/lib/server/routes.js @@ -15,7 +15,7 @@ Meteor.startup(function() { // else get test object (sample post, comment, user, etc.) const testVariables = - (typeof email.testVariables === 'function' ? email.testVariables() : email.testVariables) || {}; + (typeof email.testVariables === 'function' ? email.testVariables(params) : email.testVariables) || {}; // delete params.query so we don't pass it to GraphQL query delete params.query; // merge test variables with params from URL @@ -33,6 +33,11 @@ Meteor.startup(function() { html = ` ${builtHtml}

Subject: ${subject}

+
Variables:
+
+
${JSON.stringify(variables, null, 2)}
+
+
Data:
${JSON.stringify(emailTestData, null, 2)}
diff --git a/packages/vulcan-email/package.js b/packages/vulcan-email/package.js index a1d1888cd..2734e499c 100644 --- a/packages/vulcan-email/package.js +++ b/packages/vulcan-email/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:email', summary: 'Vulcan email package', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -10,7 +10,7 @@ Package.onUse(function (api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:lib@1.12.8' + 'vulcan:lib@1.12.13' ]); api.mainModule('lib/server.js', 'server'); diff --git a/packages/vulcan-embed/lib/server/integrations/builtin.js b/packages/vulcan-embed/lib/server/integrations/builtin.js index 56011d80f..bf2119d8d 100644 --- a/packages/vulcan-embed/lib/server/integrations/builtin.js +++ b/packages/vulcan-embed/lib/server/integrations/builtin.js @@ -27,10 +27,10 @@ Embed.builtin = { title: metadata.title, description: metadata.description, thumbnailUrl: metadata.image, - } + }; } -} +}; // -------------- // // adapted from https://github.com/acemtp/meteor-meta-extractor/blob/master/meta-extractor.js diff --git a/packages/vulcan-embed/lib/server/integrations/embedapi.js b/packages/vulcan-embed/lib/server/integrations/embedapi.js index 3ca70c4e3..4d9cf3804 100644 --- a/packages/vulcan-embed/lib/server/integrations/embedapi.js +++ b/packages/vulcan-embed/lib/server/integrations/embedapi.js @@ -40,7 +40,7 @@ if (settings) { const embedData = { title: data.title, description: data.description - } + }; if (data.pics && data.pics.length > 0) { embedData.thumbnailUrl = data.pics[0]; @@ -63,7 +63,7 @@ if (settings) { } }, - } + }; } diff --git a/packages/vulcan-embed/lib/server/integrations/embedly.js b/packages/vulcan-embed/lib/server/integrations/embedly.js index caee6ce94..f6151924a 100644 --- a/packages/vulcan-embed/lib/server/integrations/embedly.js +++ b/packages/vulcan-embed/lib/server/integrations/embedly.js @@ -38,7 +38,7 @@ if (settings) { if (data.images && data.images.length > 0) // there may not always be an image - data.thumbnailUrl = data.images[0].url.replace('http:','') // add thumbnailUrl as its own property + data.thumbnailUrl = data.images[0].url.replace('http:',''); // add thumbnailUrl as its own property if (data.authors && data.authors.length > 0) { data.sourceName = data.authors[0].name; @@ -60,7 +60,7 @@ if (settings) { } }, - } + }; } diff --git a/packages/vulcan-embed/lib/server/main.js b/packages/vulcan-embed/lib/server/main.js index 87e77348d..227d7a52e 100644 --- a/packages/vulcan-embed/lib/server/main.js +++ b/packages/vulcan-embed/lib/server/main.js @@ -1,7 +1,7 @@ export * from '../modules/index.js'; -import './integrations/builtin.js' -import './integrations/embedly.js' -import './integrations/embedapi.js' +import './integrations/builtin.js'; +import './integrations/embedly.js'; +import './integrations/embedapi.js'; import './mutations.js'; \ No newline at end of file diff --git a/packages/vulcan-embed/package.js b/packages/vulcan-embed/package.js index d861d739b..502435db5 100644 --- a/packages/vulcan-embed/package.js +++ b/packages/vulcan-embed/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:embed', summary: 'Vulcan Embed package', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -11,8 +11,8 @@ Package.onUse( function(api) { api.use([ 'http', - 'vulcan:core@1.12.8', - 'fourseven:scss@4.5.0' + 'vulcan:core@1.12.13', + 'fourseven:scss@4.10.0' ]); diff --git a/packages/vulcan-errors-sentry/README.md b/packages/vulcan-errors-sentry/README.md new file mode 100644 index 000000000..fdc3eec06 --- /dev/null +++ b/packages/vulcan-errors-sentry/README.md @@ -0,0 +1 @@ +Vulcan error tracking adapter for Sentry. \ No newline at end of file diff --git a/packages/vulcan-errors-sentry/lib/client/main.js b/packages/vulcan-errors-sentry/lib/client/main.js new file mode 100644 index 000000000..5d7fbade2 --- /dev/null +++ b/packages/vulcan-errors-sentry/lib/client/main.js @@ -0,0 +1,2 @@ +export * from '../modules/index'; +import './sentry-client.js'; \ No newline at end of file diff --git a/packages/vulcan-errors-sentry/lib/client/sentry-client.js b/packages/vulcan-errors-sentry/lib/client/sentry-client.js new file mode 100644 index 000000000..9e0077ae0 --- /dev/null +++ b/packages/vulcan-errors-sentry/lib/client/sentry-client.js @@ -0,0 +1,52 @@ +import { getSetting } from 'meteor/vulcan:core'; +import { addInitFunction, addLogFunction, addUserFunction } from 'meteor/vulcan:errors'; +import Sentry from '@sentry/browser'; +import { clientDSNSetting } from '../modules/settings'; +import { getUserObject } from '../modules/sentry'; + +const clientDSN = getSetting(clientDSNSetting); +const environment = getSetting('environment'); + +/* + +Initialize Sentry + +*/ +function initSentryForClient(props = {}) { + Sentry.init({ + dsn: clientDSN, + environment, + release: props.siteData && props.siteData.sourceVersion + }); +} +addInitFunction(initSentryForClient); + +/* + +Log an error, and optionally set current user as well + +*/ +function logToSentry({ error, details, currentUser }) { + Sentry.withScope(scope => { + if (currentUser) { + scope.setUser(getUserObject(currentUser)); + } + Object.keys(details).forEach(key => { + scope.setExtra(key, details[key]); + }); + Sentry.captureException(error); + }); +} +addLogFunction(logToSentry); + +/* + +Set the current user + +*/ +function setSentryUser(currentUser) { + Sentry.configureScope(scope => { + scope.setUser(getUserObject(currentUser)); + }); +} +addUserFunction(setSentryUser); diff --git a/packages/vulcan-errors-sentry/lib/modules/index.js b/packages/vulcan-errors-sentry/lib/modules/index.js new file mode 100644 index 000000000..81f4fab9a --- /dev/null +++ b/packages/vulcan-errors-sentry/lib/modules/index.js @@ -0,0 +1,2 @@ +export * from './settings'; +// import './logToRollbar'; diff --git a/packages/vulcan-errors-sentry/lib/modules/sentry.js b/packages/vulcan-errors-sentry/lib/modules/sentry.js new file mode 100644 index 000000000..0d0edda46 --- /dev/null +++ b/packages/vulcan-errors-sentry/lib/modules/sentry.js @@ -0,0 +1,5 @@ +export const getUserObject = currentUser => ({ + id: currentUser._id, + username: currentUser.displayName, + email: currentUser.email, +}); \ No newline at end of file diff --git a/packages/vulcan-errors-sentry/lib/modules/settings.js b/packages/vulcan-errors-sentry/lib/modules/settings.js new file mode 100644 index 000000000..76312ee9d --- /dev/null +++ b/packages/vulcan-errors-sentry/lib/modules/settings.js @@ -0,0 +1,8 @@ +import { registerSetting } from 'meteor/vulcan:core'; + +export const clientDSNSetting = 'sentry.clientDSN'; +export const serverDSNSetting = 'sentry.serverDSN'; +export const tokensUrl = 'https://sentry.io/onboarding/{account}/{project}/configure/node'; + +registerSetting(clientDSNSetting, null, `Sentry client DSN access token (from ${tokensUrl})`); +registerSetting(serverDSNSetting, null, `Sentry client DSN access token (from ${tokensUrl})`); diff --git a/packages/vulcan-errors-sentry/lib/server/main.js b/packages/vulcan-errors-sentry/lib/server/main.js new file mode 100644 index 000000000..0d7733f2d --- /dev/null +++ b/packages/vulcan-errors-sentry/lib/server/main.js @@ -0,0 +1,2 @@ +import './sentry-server.js'; +export * from '../modules/index'; diff --git a/packages/vulcan-errors-sentry/lib/server/sentry-server.js b/packages/vulcan-errors-sentry/lib/server/sentry-server.js new file mode 100644 index 000000000..2c9ba1bc2 --- /dev/null +++ b/packages/vulcan-errors-sentry/lib/server/sentry-server.js @@ -0,0 +1,53 @@ +import { getSetting, getSourceVersion } from 'meteor/vulcan:core'; +import { addInitFunction, addLogFunction, addUserFunction } from 'meteor/vulcan:errors'; +import { serverDSNSetting } from '../modules/settings'; +import Sentry from '@sentry/node'; +import { getUserObject } from '../modules/sentry'; + +const serverDSN = getSetting(serverDSNSetting); +const environment = getSetting('environment'); + +/* + +Initialize Sentry + +*/ +function initSentryForServer() { + Sentry.init({ + dsn: serverDSN, + environment, + // see https://github.com/zodern/meteor-up/issues/807#issuecomment-346915622 + release: getSourceVersion(), + }); +} +addInitFunction(initSentryForServer); + +/* + +Log an error, and optionally set current user as well + +*/ +function logToSentry({ error, details, currentUser }) { + Sentry.withScope(scope => { + if (currentUser) { + scope.setUser(getUserObject(currentUser)); + } + Object.keys(details).forEach(key => { + scope.setExtra(key, details[key]); + }); + Sentry.captureException(error); + }); +} +addLogFunction(logToSentry); + +/* + +Set the current user + +*/ +function setSentryUser(currentUser) { + Sentry.configureScope(scope => { + scope.setUser(getUserObject(currentUser)); + }); +} +addUserFunction(setSentryUser); diff --git a/packages/vulcan-errors-sentry/package.js b/packages/vulcan-errors-sentry/package.js new file mode 100755 index 000000000..57131816b --- /dev/null +++ b/packages/vulcan-errors-sentry/package.js @@ -0,0 +1,22 @@ +Package.describe({ + name: 'vulcan:errors-sentry', + summary: 'Vulcan Sentry error tracking package', + version: '1.12.13', + git: 'https://github.com/VulcanJS/Vulcan.git' +}); + + +Package.onUse(function(api) { + api.versionsFrom('1.6.1'); + + api.use([ + 'ecmascript', + 'vulcan:core@1.12.13', + 'vulcan:users@1.12.13', + 'vulcan:errors@1.12.13', + ]); + + api.mainModule('lib/server/main.js', 'server'); + api.mainModule('lib/client/main.js', 'client'); + +}); diff --git a/packages/vulcan-errors/README.md b/packages/vulcan-errors/README.md new file mode 100644 index 000000000..24ad18cef --- /dev/null +++ b/packages/vulcan-errors/README.md @@ -0,0 +1 @@ +Vulcan error tracking package. \ No newline at end of file diff --git a/packages/vulcan-errors/lib/client/init.js b/packages/vulcan-errors/lib/client/init.js new file mode 100644 index 000000000..c8fae93e6 --- /dev/null +++ b/packages/vulcan-errors/lib/client/init.js @@ -0,0 +1,14 @@ +import { addCallback } from 'meteor/vulcan:core'; +import { initFunctions } from '../modules/index.js'; + +// on client, init function will be executed once App is ready +export const addInitFunction = fn => { + initFunctions.push(fn); +}; + +function runInitFunctions(props) { + initFunctions.forEach(f => { + f(props); + }); +} +addCallback('app.mounted', runInitFunctions); diff --git a/packages/vulcan-errors/lib/client/main.js b/packages/vulcan-errors/lib/client/main.js new file mode 100644 index 000000000..f728cfadc --- /dev/null +++ b/packages/vulcan-errors/lib/client/main.js @@ -0,0 +1,2 @@ +export * from './init.js'; +export * from '../modules/index.js'; \ No newline at end of file diff --git a/packages/vulcan-errors/lib/components/ErrorCatcher.jsx b/packages/vulcan-errors/lib/components/ErrorCatcher.jsx new file mode 100644 index 000000000..b8be2f322 --- /dev/null +++ b/packages/vulcan-errors/lib/components/ErrorCatcher.jsx @@ -0,0 +1,46 @@ +/* + +ErrorCatcher + +Usage: + + + + + +*/ + +import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core'; +import React, { Component } from 'react'; +import { Errors } from '../modules/errors.js'; + +class ErrorCatcher extends Component { + state = { + error: null, + }; + + componentDidCatch = (error, errorInfo) => { + const { currentUser, siteData = {} } = this.props; + const { sourceVersion } = siteData; + this.setState({ error }); + Errors.log({ + message: error.message, + error, + details: { ...errorInfo, sourceVersion }, + currentUser, + }); + }; + + render() { + const { error } = this.state; + return error ? ( +
+ +
+ ) : ( + this.props.children + ); + } +} + +registerComponent('ErrorCatcher', ErrorCatcher, withCurrentUser); diff --git a/packages/vulcan-errors/lib/components/ErrorsUserMonitor.jsx b/packages/vulcan-errors/lib/components/ErrorsUserMonitor.jsx new file mode 100644 index 000000000..c5b01e2c8 --- /dev/null +++ b/packages/vulcan-errors/lib/components/ErrorsUserMonitor.jsx @@ -0,0 +1,58 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core'; +import classNames from 'classnames'; +import { Errors } from 'meteor/vulcan:errors'; + +class ErrorsUserMonitor extends PureComponent { + constructor(props) { + super(props); + } + + componentDidMount() { + this.checkCurrentUser(); + } + + componentDidUpdate() { + this.checkCurrentUser(); + } + + checkCurrentUser(prevProps, prevState, snapshot) { + const currentUser = this.props.currentUser; + + const currentUserId = currentUser && currentUser._id; + const errorsUserId = Errors.currentUser && Errors.currentUser._id; + + if (currentUserId !== errorsUserId) { + // const currentUserEmail = currentUser && currentUser.email; + // const errorsUserEmail = Errors.currentUser && Errors.currentUser.email; + // console.log(`User changed from ${errorsUserEmail} (${errorsUserId}) to ${currentUserEmail} (${currentUserId})`); + + Errors.setCurrentUser(currentUser); + } + } + + render() { + const { className, currentUser } = this.props; + + return ( +
+ ); + } +} + +ErrorsUserMonitor.propTypes = { + className: PropTypes.string, + currentUser: PropTypes.object, +}; + +ErrorsUserMonitor.displayName = 'ErrorsUserMonitor'; + +registerComponent('ErrorsUserMonitor', ErrorsUserMonitor, withCurrentUser); diff --git a/packages/vulcan-errors/lib/modules/errors.js b/packages/vulcan-errors/lib/modules/errors.js new file mode 100644 index 000000000..fffc6e48f --- /dev/null +++ b/packages/vulcan-errors/lib/modules/errors.js @@ -0,0 +1,202 @@ +// import Users from 'meteor/vulcan:users'; +// import { getSetting } from 'meteor/vulcan:core'; +// import get from 'lodash/get'; +import isEqual from 'lodash/isEqual'; +// import { formatMessage } from 'meteor/vulcan:i18n'; +// import _isEmpty from 'lodash/isEmpty'; +// import { inspect } from 'util'; + +export const initFunctions = []; +export const logFunctions = []; +export const userFunctions = []; +export const scrubFields = new Set(); + +export const userFields = { + id: '_id', + email: 'email', + username: 'profile.username', + isAdmin: 'isAdmin', +}; + +/* + +Moved to server/client's init.js + +*/ +// export const addInitFunction = fn => { +// initFunctions.push(fn); +// // on server, execute init function as soon as possible +// fn(); +// }; + +export const addLogFunction = fn => { + logFunctions.push(fn); +}; + +export const addUserFunction = fn => { + userFunctions.push(fn); +}; + +export const addUserFields = fields => { + Object.assign(userFields, fields); +}; + +export const addScrubFields = fields => { + fields = Array.isArray(fields) ? fields : [fields]; + for (const field of fields) { + scrubFields.add(field); + } +}; + +// export const getUserPayload = function(userOrUserId) { +// try { +// const user = Users.getUser(userOrUserId); +// if (!user) return null; + +// const userPayload = {}; + +// for (const field in userFields) { +// const path = userFields[field]; +// userPayload[field] = get(user, path); +// } + +// return userPayload; +// } catch (error) { +// return null; +// } +// }; + +// export const getServerHost = function() { +// return process.env.GALAXY_CONTAINER_ID +// ? process.env.GALAXY_CONTAINER_ID.split('-')[1] +// : getSetting('public.environment'); +// }; + +// export const processApolloErrors = function(err) { +// if (!err) return; + +// const apolloErrors = +// err.original && err.original.data && err.original.data.errors +// ? formatApolloError(err.original, formatMessage, '\n', ' ApolloError: ') +// : err.data && err.data.errors +// ? formatApolloError(err, formatMessage, '\n', ' ApolloError: ') +// : ''; + +// err.message = err.message + '\n' + apolloErrors; +// }; + +// export const formatApolloError = (err, formatMessage, separator = ', ', prefix = '') => { +// let formatted = ''; + +// const formatProperties = properties => { +// return _isEmpty(properties) ? '' : ' ' + inspect(properties); +// }; + +// const addError = error => { +// let message = ''; + +// if (error.id) { +// try { +// message = formatMessage({ id: error.id }, error.properties); +// } catch (err) { +// message = error.id + formatProperties(error.properties); +// } +// } else if (error.message) { +// message = error.message + formatProperties(error.properties); +// } + +// formatted += formatted ? separator : ''; +// formatted += prefix + message; +// }; + +// const graphQLErrors = err.data && err.data.errors ? [err] : err.graphQLErrors; + +// if (graphQLErrors) { +// for (let graphQLError of graphQLErrors) { +// if (graphQLError.data && graphQLError.data.errors) { +// for (let innerError of graphQLError.data.errors) { +// if (innerError.data) { +// addError(innerError.data); +// } else { +// addError(innerError); +// } +// } +// } else if (graphQLError.data) { +// addError(graphQLError.data); +// } else { +// addError(graphQLError); +// } +// } +// } else { +// let message = err.message; +// const graphqlPrefixIsPresent = message.match(/GraphQL error: (.*)/); +// addError({ message: graphqlPrefixIsPresent ? graphqlPrefixIsPresent[1] : message }); +// } + +// return formatted; +// }; + +export const Errors = { + currentUser: null, + + setCurrentUser: function(user) { + // avoid setting current user multiple times + if (isEqual(this.currentUser, user)) return; + + for (const fn of userFunctions) { + try { + fn(user); + } catch (error) { + // eslint-disable-next-line no-console + console.log(`// ${fn.name} with ${user && user.email}`); + // eslint-disable-next-line no-console + console.log(error); + } + } + + this.currentUser = user; + }, + + /*rethrow: function (message, err, details, level = 'error') { + err = new RethrownError(message, err, { stack: true, remove: 1 }); + Errors.log({ err, details, level }); + },*/ + + log: function(params) { + const { message, err, level = 'error' } = params; + // processApolloErrors(err); + + for (const fn of logFunctions) { + try { + fn(params); + } catch (error) { + // eslint-disable-next-line no-console + console.log(`// ${fn.name} ${level} error for '${(err && err.message) || message}'`); + // eslint-disable-next-line no-console + console.log(error); + } + } + }, + + /* + + Shortcuts + + */ + debug: params => Errors.log({ level: 'debug', ...params }), + + info: params => Errors.log({ level: 'info', ...params }), + + warning: params => Errors.log({ level: 'warning', ...params }), + + error: params => Errors.log({ level: 'error', ...params }), + + critical: params => Errors.log({ level: 'info', ...params }), + + // getMethodDetails: function(method) { + // return { + // userId: method.userId, + // headers: method.connection && method.connection.httpHeaders, + // }; + // }, +}; \ No newline at end of file diff --git a/packages/vulcan-errors/lib/modules/extended-NOTUSED.js b/packages/vulcan-errors/lib/modules/extended-NOTUSED.js new file mode 100644 index 000000000..ca33a3160 --- /dev/null +++ b/packages/vulcan-errors/lib/modules/extended-NOTUSED.js @@ -0,0 +1,50 @@ +// This is experimental and not actually used by vulcan:errors + +// ### ExtendedError +// From https://github.com/deployable/deployable-errors + +// Custom errors can extend this + +export default class ExtendedError extends Error { + constructor(message, options = {}) { + // Make it an error + super(message); + + // Standard Error things + this.name = this.constructor.name; + this.message = message; + + // Get a stack trace where we can + /* istanbul ignore else */ + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, this.constructor); + } else { + this.stack = new Error(message).stack; + } + + // A standard place to store a more human readable error message + if (options.simple) this.simple = options.simple; + } + + // Support `.statusCode` for express + get statusCode() { + return this.status; + } + + set statusCode(val) { + this.status = val; + } + + // Fix Errors `.toJSON` for our errors + toJSON() { + let o = {}; + Object.getOwnPropertyNames(this).forEach(key => (o[key] = this[key]), this); + return o; + } + + toResponse() { + let o = this.toJSON(); + if (process && process.env && process.env.NODE_ENV !== 'development') delete o.stack; + return o; + } +} diff --git a/packages/vulcan-errors/lib/modules/index.js b/packages/vulcan-errors/lib/modules/index.js new file mode 100644 index 000000000..5d60bbdef --- /dev/null +++ b/packages/vulcan-errors/lib/modules/index.js @@ -0,0 +1,3 @@ +import '../components/ErrorsUserMonitor'; +import '../components/ErrorCatcher'; +export * from './errors.js'; \ No newline at end of file diff --git a/packages/vulcan-errors/lib/modules/rethrown-NOTUSED.js b/packages/vulcan-errors/lib/modules/rethrown-NOTUSED.js new file mode 100644 index 000000000..9390eeb3b --- /dev/null +++ b/packages/vulcan-errors/lib/modules/rethrown-NOTUSED.js @@ -0,0 +1,45 @@ +import ExtendedError from './extended'; + +// This is experimental and not actually used by vulcan:errors + +/** + * Rethrow an error that you caught in your code, adding an additional message, + * and preserving the stack trace + * + * Based on https://github.com/deployable/deployable-errors + * See https://stackoverflow.com/questions/42754270/re-throwing-exception-in-nodejs-and-not-losing-stack-trace + * + * @example + * try { + * ... some code + * } catch (error) { + * new RethrownError('new error message', error, { stack: true }); + * } + */ +export default class RethrownError extends ExtendedError { + /** + * @param {string} message - An error message + * @param {Error} error - An Error caught in a catch block + * @param {Object} [options] - The employee who is responsible for the project. + * @param {boolean|number} [options.stack] - Enable, disable or set the number of lines of stack output + * @param {number} [options.remove] - The number of lines to remove from the beginning of the stack trace + */ + constructor(message, error, options = {}) { + super(message); + if (!error) throw new Error(`new ${this.name} requires a message and error`); + + let message_lines = (this.message.match(/\n/g) || []).length + 1; + let stack_array = this.stack.split('\n'); + + if (options.remove) { + stack_array.splice(message_lines, options.remove); + } + + if (options.stack !== true) { + stack_array = stack_array.slice(0, message_lines + (options.stack || 0)); + } + + //this.original = error; + this.stack = stack_array.join('\n') + '\n' + error.stack; + } +} diff --git a/packages/vulcan-errors/lib/server/init.js b/packages/vulcan-errors/lib/server/init.js new file mode 100644 index 000000000..e0c16a1cc --- /dev/null +++ b/packages/vulcan-errors/lib/server/init.js @@ -0,0 +1,7 @@ +import { initFunctions } from '../modules/index.js'; + +export const addInitFunction = fn => { + initFunctions.push(fn); // on server, this does nothing + // on server, execute init function as soon as possible + fn(); +}; \ No newline at end of file diff --git a/packages/vulcan-errors/lib/server/main.js b/packages/vulcan-errors/lib/server/main.js new file mode 100644 index 000000000..33f86ecc8 --- /dev/null +++ b/packages/vulcan-errors/lib/server/main.js @@ -0,0 +1,2 @@ +export * from './init.js'; +export * from '../modules/index.js'; diff --git a/packages/vulcan-errors/package.js b/packages/vulcan-errors/package.js new file mode 100644 index 000000000..6b62e9e65 --- /dev/null +++ b/packages/vulcan-errors/package.js @@ -0,0 +1,20 @@ +Package.describe({ + name: 'vulcan:errors', + summary: 'Vulcan error tracking package', + version: '1.12.13', + git: 'https://github.com/VulcanJS/Vulcan.git' +}); + +Package.onUse(function(api) { + + api.versionsFrom('1.6.1'); + + api.use([ + 'ecmascript', + 'vulcan:core@1.12.13', + ]); + + api.mainModule('lib/server/main.js', 'server'); + api.mainModule('lib/client/main.js', 'client'); + +}); diff --git a/packages/vulcan-events-ga/lib/client/ga.js b/packages/vulcan-events-ga/lib/client/ga.js index 6eb126937..a08eb0bd7 100644 --- a/packages/vulcan-events-ga/lib/client/ga.js +++ b/packages/vulcan-events-ga/lib/client/ga.js @@ -1,5 +1,5 @@ import { getSetting } from 'meteor/vulcan:core'; -import { addPageFunction, addInitFunction } from 'meteor/vulcan:events'; +import { addPageFunction, addInitFunction, addTrackFunction } from 'meteor/vulcan:events'; /* @@ -19,10 +19,25 @@ function googleAnaticsTrackPage() { } return {}; } - // add client-side callback: log a ga request on page view addPageFunction(googleAnaticsTrackPage); +function googleAnaticsTrackEvent(name, properties, currentUser) { + const { category = name, action = name, label = name, value } = properties; + if (window && window.ga) { + window.ga('send', { + hitType: 'event', + eventCategory: category, + eventAction: action, + eventLabel: label, + eventValue: value, + }); + } + return {}; +} +// add client-side callback: log a ga request on page view +addTrackFunction(googleAnaticsTrackEvent); + function googleAnalyticsInit() { // get the google analytics id from the settings const googleAnalyticsId = getSetting('googleAnalytics.apiKey'); diff --git a/packages/vulcan-events-ga/package.js b/packages/vulcan-events-ga/package.js index 7fe0baee9..fca9079cb 100644 --- a/packages/vulcan-events-ga/package.js +++ b/packages/vulcan-events-ga/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:events-ga', summary: 'Vulcan Google Analytics event tracking package', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -10,8 +10,8 @@ Package.onUse(function(api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:core@1.12.8', - 'vulcan:events@1.12.8', + 'vulcan:core@1.12.13', + 'vulcan:events@1.12.13', ]); api.mainModule('lib/server/main.js', 'server'); diff --git a/packages/vulcan-events-intercom/package.js b/packages/vulcan-events-intercom/package.js index 9b5f51030..24c210fe0 100644 --- a/packages/vulcan-events-intercom/package.js +++ b/packages/vulcan-events-intercom/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:events-intercom', summary: 'Vulcan Intercom integration package.', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -10,8 +10,8 @@ Package.onUse(function (api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:core@1.12.8', - 'vulcan:events@1.12.8' + 'vulcan:core@1.12.13', + 'vulcan:events@1.12.13' ]); api.mainModule('lib/client/main.js', 'client'); diff --git a/packages/vulcan-events-internal/lib/modules/collection.js b/packages/vulcan-events-internal/lib/modules/collection.js index fb69a368c..dea63e2bc 100644 --- a/packages/vulcan-events-internal/lib/modules/collection.js +++ b/packages/vulcan-events-internal/lib/modules/collection.js @@ -23,6 +23,6 @@ const Events = createCollection({ Events.checkAccess = (currentUser, doc) => { return Users.isAdmin(currentUser); -} +}; export default Events; diff --git a/packages/vulcan-events-internal/lib/modules/schema.js b/packages/vulcan-events-internal/lib/modules/schema.js index 1a5582535..2692cfe0f 100644 --- a/packages/vulcan-events-internal/lib/modules/schema.js +++ b/packages/vulcan-events-internal/lib/modules/schema.js @@ -4,7 +4,7 @@ const schema = { canRead: ['guests'], optional: true, onInsert: () => { - return new Date() + return new Date(); } }, name: { diff --git a/packages/vulcan-events-internal/package.js b/packages/vulcan-events-internal/package.js index d9071e567..652ab3c59 100644 --- a/packages/vulcan-events-internal/package.js +++ b/packages/vulcan-events-internal/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:events-internal', summary: 'Vulcan internal event tracking package', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -10,8 +10,8 @@ Package.onUse(function(api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:core@1.12.8', - 'vulcan:events@1.12.8', + 'vulcan:core@1.12.13', + 'vulcan:events@1.12.13', ]); api.mainModule('lib/server/main.js', 'server'); diff --git a/packages/vulcan-events-segment/package.js b/packages/vulcan-events-segment/package.js index 74a5c632f..42a40f9ea 100644 --- a/packages/vulcan-events-segment/package.js +++ b/packages/vulcan-events-segment/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:events-segment', summary: 'Vulcan Segment', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -10,8 +10,8 @@ Package.onUse(function (api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:core@1.12.8', - 'vulcan:events@1.12.8', + 'vulcan:core@1.12.13', + 'vulcan:events@1.12.13', ]); api.mainModule('lib/server/main.js', 'server'); diff --git a/packages/vulcan-events/lib/modules/events.js b/packages/vulcan-events/lib/modules/events.js index dadfcb923..5d435e6ae 100644 --- a/packages/vulcan-events/lib/modules/events.js +++ b/packages/vulcan-events/lib/modules/events.js @@ -37,7 +37,7 @@ export const addIdentifyFunction = f => { }; export const addPageFunction = f => { - const f2 = (empty, route) => f(route); + const f2 = ({ currentRoute }) => f(currentRoute); // rename f2 to same name as f // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty @@ -45,5 +45,5 @@ export const addPageFunction = f => { descriptor.value = f.name; Object.defineProperty(f2, 'name', descriptor); - addCallback('router.onUpdate', f2); + addCallback('router.onUpdate.async', f2); }; diff --git a/packages/vulcan-events/package.js b/packages/vulcan-events/package.js index 8c10602b0..d24e67f3d 100644 --- a/packages/vulcan-events/package.js +++ b/packages/vulcan-events/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:events', summary: 'Vulcan event tracking package', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -10,7 +10,7 @@ Package.onUse(function(api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:core@1.12.8', + 'vulcan:core@1.12.13', ]); api.mainModule('lib/server/main.js', 'server'); diff --git a/packages/vulcan-forms-tags/lib/components/Tags.jsx b/packages/vulcan-forms-tags/lib/components/Tags.jsx index 635832806..53358f7f2 100644 --- a/packages/vulcan-forms-tags/lib/components/Tags.jsx +++ b/packages/vulcan-forms-tags/lib/components/Tags.jsx @@ -1,98 +1,46 @@ import React, { PureComponent } from 'react'; -import FRC from 'formsy-react-components'; import ReactTagInput from 'react-tag-input'; import PropTypes from 'prop-types'; const ReactTags = ReactTagInput.WithContext; -const Input = FRC.Input; - class Tags extends PureComponent { constructor(props) { super(props); - this.handleDelete = this.handleDelete.bind(this); - this.handleAddition = this.handleAddition.bind(this); - const tags = props.value ? props.value.map(optionId => { - return { - id: optionId, - text: _.findWhere(props.options, {value: optionId}).label - }; - }) : []; + this.suggestions = (props.options || []).map( + ({ value, label }) => ({ id: value, text: label }) + ); + const tags = (props.value || []).map(id => ( + // tolerate cases when a tag is not found in suggestions (create a tag on the fly) + this.suggestions.find(suggestion => id === suggestion.id) || { id, text: id } + )); - this.state = { - tags: tags, - suggestions: _.pluck(props.options, 'label'), - value: props.value || [] - }; + this.state = { tags }; } - handleDelete(i) { - - const tags = this.state.tags; - tags.splice(i, 1); - - const value = this.state.value; - value.splice(i,1); - - this.setState({ - tags: tags, - value: value - }); - } - - handleAddition(tag) { - - // first, check if added tag is part of the possible options - const option = _.findWhere(this.props.options, {label: tag}); - - if (option) { - - // add tag to state (for tag widget) - const tags = this.state.tags; - tags.push({ - id: tags.length + 1, - text: tag - }); - - // add value to state (to store in db) - const value = this.state.value; - value.push(option.value); - - this.setState({ - tags: tags, - value: value - }); - } - + handleChange = reducer => value => { + const tags = reducer(this.state.tags, value); + this.setState({ tags }); + this.props.inputProperties.onChange(this.props.name, tags.map(({ id }) => id)); } render() { - - const {name, /* value, */ label} = this.props; - return (
- +
[...tags.slice(0, index), ...tags.slice(index + 1)] + )} + handleAddition={this.handleChange((tags, newTag) => [...tags, newTag])} minQueryLength={1} - classNames={{ - // tags: 'tagsClass', - // tagInput: 'form-control' - // selected: 'selectedClass', - // tag: 'tagClass', - // remove: 'removeClass', - // suggestions: 'suggestionsClass' - }} /> -
@@ -103,7 +51,16 @@ class Tags extends PureComponent { Tags.propTypes = { name: PropTypes.string, value: PropTypes.any, - label: PropTypes.string + label: PropTypes.string, + inputProperties: PropTypes.shape({ + onChange: PropTypes.func, + }), + options: PropTypes.arrayOf( + PropTypes.shape({ + value: PropTypes.any, + label: PropTypes.string, + }) + ), }; export default Tags; diff --git a/packages/vulcan-forms-tags/package.js b/packages/vulcan-forms-tags/package.js index 065af81e8..314df40e2 100644 --- a/packages/vulcan-forms-tags/package.js +++ b/packages/vulcan-forms-tags/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:forms-tags', summary: 'Vulcan tag input package', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -10,8 +10,8 @@ Package.onUse( function(api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:core@1.12.8', - 'vulcan:forms@1.12.8' + 'vulcan:core@1.12.13', + 'vulcan:forms@1.12.13' ]); api.mainModule('lib/export.js', ['client', 'server']); diff --git a/packages/vulcan-forms-upload/package.js b/packages/vulcan-forms-upload/package.js index e9e857d58..478b2cc5c 100755 --- a/packages/vulcan-forms-upload/package.js +++ b/packages/vulcan-forms-upload/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:forms-upload', summary: 'Vulcan package extending vulcan:forms to upload images to Cloudinary from a drop zone.', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/xavcz/nova-forms-upload.git' }); @@ -10,9 +10,9 @@ Package.onUse( function(api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:core@1.12.8', - 'vulcan:forms@1.12.8', - 'fourseven:scss@4.5.0' + 'vulcan:core@1.12.13', + 'vulcan:forms@1.12.13', + 'fourseven:scss@4.10.0' ]); api.addFiles([ diff --git a/packages/vulcan-forms/lib/components/FieldErrors.jsx b/packages/vulcan-forms/lib/components/FieldErrors.jsx index a93597970..4012936ac 100644 --- a/packages/vulcan-forms/lib/components/FieldErrors.jsx +++ b/packages/vulcan-forms/lib/components/FieldErrors.jsx @@ -11,4 +11,7 @@ const FieldErrors = ({ errors }) => ( ))} ); +FieldErrors.propTypes = { + errors: PropTypes.array.isRequired +}; registerComponent('FieldErrors', FieldErrors); diff --git a/packages/vulcan-forms/lib/components/Form.jsx b/packages/vulcan-forms/lib/components/Form.jsx index 53f3a2f65..e4dc8ee2a 100644 --- a/packages/vulcan-forms/lib/components/Form.jsx +++ b/packages/vulcan-forms/lib/components/Form.jsx @@ -26,17 +26,17 @@ import { registerComponent, Components, runCallbacks, - getCollection, getErrors, getSetting, Utils, isIntlField, + mergeWithComponents } from 'meteor/vulcan:core'; import React, { Component } from 'react'; +import SimpleSchema from 'simpl-schema'; import PropTypes from 'prop-types'; import { intlShape } from 'meteor/vulcan:i18n'; import Formsy from 'formsy-react'; -import { getEditableFields, getInsertableFields } from '../modules/utils.js'; import cloneDeep from 'lodash/cloneDeep'; import get from 'lodash/get'; import set from 'lodash/set'; @@ -57,29 +57,65 @@ import pickBy from 'lodash/pickBy'; import { convertSchema, formProperties } from '../modules/schema_utils'; import { isEmptyValue } from '../modules/utils'; import { getParentPath } from '../modules/path_utils'; +import { + getEditableFields, + getInsertableFields +} from '../modules/schema_utils.js'; +import withCollectionProps from './withCollectionProps'; +import { callbackProps } from './propTypes'; + + +// props that should trigger a form reset +const RESET_PROPS = [ + 'collection', 'collectionName', 'typeName', 'document', 'schema', 'currentUser', + 'fields', 'removeFields', + 'prefilledProps' // TODO: prefilledProps should be merged instead? +]; const compactParent = (object, path) => { const parentPath = getParentPath(path); // note: we only want to compact arrays, not objects - const compactIfArray = x => Array.isArray(x) ? compact(x) : x; + const compactIfArray = x => (Array.isArray(x) ? compact(x) : x); update(object, parentPath, compactIfArray); }; const getDefaultValues = convertedSchema => { // TODO: make this work with nested schemas, too - return pickBy(mapValues(convertedSchema, field => field.defaultValue), value => value); -} + return pickBy( + mapValues(convertedSchema, field => field.defaultValue), + value => value + ); +}; -const getInitialStateFromProps = (nextProps) => { - const collection = nextProps.collection || getCollection(nextProps.collectionName); - const schema = collection.simpleSchema(); +const getInitialStateFromProps = nextProps => { + const collection = nextProps.collection; + const schema = nextProps.schema + ? new SimpleSchema(nextProps.schema) + : collection.simpleSchema(); const convertedSchema = convertSchema(schema); const formType = nextProps.document ? 'edit' : 'new'; // for new document forms, add default values to initial document - const defaultValues = formType === 'new' ? getDefaultValues(convertedSchema) : {}; - const initialDocument = merge({}, defaultValues, nextProps.prefilledProps, nextProps.document); + const defaultValues = + formType === 'new' ? getDefaultValues(convertedSchema) : {}; + const initialDocument = merge( + {}, + defaultValues, + nextProps.prefilledProps, + nextProps.document + ); + + //if minCount is specified, go ahead and create empty nested documents + Object.keys(convertedSchema).forEach(key => { + let minCount = convertedSchema[key].minCount; + if(minCount) { + initialDocument[key] = initialDocument[key] || []; + while(initialDocument[key].length < minCount) + initialDocument[key].push({}); + } + }); + // remove all instances of the `__typename` property from document Utils.removeProperty(initialDocument, '__typename'); @@ -95,7 +131,7 @@ const getInitialStateFromProps = (nextProps) => { // the initial document passed as props initialDocument, // initialize the current document to be the same as the initial document - currentDocument: initialDocument, + currentDocument: initialDocument }; }; @@ -115,11 +151,11 @@ class SmartForm extends Component { super(props); this.state = { - ...getInitialStateFromProps(props), + ...getInitialStateFromProps(props) }; } - defaultValues = {}; + defaultValues = {}; submitFormCallbacks = []; successFormCallbacks = []; @@ -130,58 +166,43 @@ class SmartForm extends Component { // --------------------------------------------------------------------- // /* - - Get the current collection - - */ - getCollection = () => { - return this.props.collection || getCollection(this.props.collectionName); - }; - - /* - - Get current typeName - - */ - getTypeName = () => { - return this.getCollection().options.typeName; - } - - /* - If a document is being passed, this is an edit form - */ getFormType = () => { return this.props.document ? 'edit' : 'new'; }; /* - Get a list of all insertable fields - */ - getInsertableFields = (schema) => { - return getInsertableFields(schema || this.state.schema, this.props.currentUser); - } + getInsertableFields = schema => { + return getInsertableFields( + schema || this.state.schema, + this.props.currentUser + ); + }; /* - Get a list of all editable fields - */ - getEditableFields = (schema) => { - return getEditableFields(schema || this.state.schema, this.props.currentUser, this.state.initialDocument) - } + getEditableFields = schema => { + return getEditableFields( + schema || this.state.schema, + this.props.currentUser, + this.state.initialDocument + ); + }; /* Get a list of all mutable (insertable/editable depending on current form type) fields */ - getMutableFields = (schema) => { - return this.getFormType() === 'edit' ? this.getEditableFields(schema) : this.getInsertableFields(schema); - } + getMutableFields = schema => { + return this.getFormType() === 'edit' + ? this.getEditableFields(schema) + : this.getInsertableFields(schema); + }; /* @@ -200,9 +221,13 @@ class SmartForm extends Component { Also remove any deleted values. */ - getData = (customArgs) => { - - const args = { excludeHiddenFields: false, replaceIntlFields: true, addExtraFields: false, ...customArgs }; + getData = customArgs => { + const args = { + excludeHiddenFields: false, + replaceIntlFields: true, + addExtraFields: false, + ...customArgs + }; // only keep relevant fields // for intl fields, make sure we look in foo_intl and not foo @@ -226,10 +251,16 @@ class SmartForm extends Component { }); // run data object through submitForm callbacks - data = runCallbacks(this.submitFormCallbacks, data); + data = runCallbacks({ callbacks: this.submitFormCallbacks, iterator: data, properties: { form: this }}); return data; }; + + /* + + Get form components, in case any has been overwritten for this specific form + + */ // --------------------------------------------------------------------- // // -------------------------------- Fields ----------------------------- // // --------------------------------------------------------------------- // @@ -253,7 +284,8 @@ class SmartForm extends Component { // for each group, add relevant fields groups = groups.map(group => { - group.label = group.label || this.context.intl.formatMessage({ id: group.name }); + group.label = + group.label || this.context.intl.formatMessage({ id: group.name }); group.fields = _.filter(fields, field => { return field.group && field.group.name === group.name; }); @@ -268,8 +300,8 @@ class SmartForm extends Component { order: 0, fields: _.filter(fields, field => { return !field.group; - }), - }, + }) + } ].concat(groups); // sort by order @@ -288,10 +320,14 @@ class SmartForm extends Component { */ getFieldNames = (args = {}) => { + const { + schema = this.state.schema, + excludeHiddenFields = true, + replaceIntlFields = false, + addExtraFields = true + } = args; - const { schema = this.state.schema, excludeHiddenFields = true, replaceIntlFields = false, addExtraFields = true } = args; - - const { fields, addFields, } = this.props; + const { fields, addFields } = this.props; // get all editable/insertable fields (depending on current form type) let relevantFields = this.getMutableFields(schema); @@ -301,14 +337,18 @@ class SmartForm extends Component { relevantFields = _.intersection(relevantFields, fields); } - // if "removeFields" prop is specified, remove its fields + // if "hideFields" prop is specified, remove its fields const removeFields = this.props.hideFields || this.props.removeFields; if (typeof removeFields !== 'undefined' && removeFields.length > 0) { relevantFields = _.difference(relevantFields, removeFields); } // if "addFields" prop is specified, add its fields - if (addExtraFields && typeof addFields !== 'undefined' && addFields.length > 0) { + if ( + addExtraFields && + typeof addFields !== 'undefined' && + addFields.length > 0 + ) { relevantFields = relevantFields.concat(addFields); } @@ -317,13 +357,18 @@ class SmartForm extends Component { const document = this.getDocument(); relevantFields = _.reject(relevantFields, fieldName => { const hidden = schema[fieldName].hidden; - return typeof hidden === 'function' ? hidden({ ...this.props, document }) : hidden; + return typeof hidden === 'function' + ? hidden({ ...this.props, document }) + : hidden; }); } // replace intl fields if (replaceIntlFields) { - relevantFields = relevantFields.map(fieldName => isIntlField(schema[fieldName]) ? `${fieldName}_intl` : fieldName); + relevantFields = relevantFields.map( + fieldName => + isIntlField(schema[fieldName]) ? `${fieldName}_intl` : fieldName + ); } // remove any duplicates @@ -332,42 +377,17 @@ class SmartForm extends Component { return relevantFields; }; - /* - Given a field's name, the containing schema, and parent, create the - complete field object to be passed to the component - - */ - createField = (fieldName, schema, parentFieldName, parentPath) => { - const fieldPath = parentPath ? `${parentPath}.${fieldName}` : fieldName; - const fieldSchema = schema[fieldName]; - + initField = (fieldName, fieldSchema) => { // intialize properties let field = { ..._.pick(fieldSchema, formProperties), document: this.state.initialDocument, name: fieldName, - path: fieldPath, datatype: fieldSchema.type, layout: this.props.layout, - input: fieldSchema.input || fieldSchema.control, + input: fieldSchema.input || fieldSchema.control }; - - // if this an intl'd field, use a special intlInput - if (isIntlField(fieldSchema)) { - field.intlInput = true; - } - - if (field.defaultValue) { - set(this.defaultValues, fieldPath, field.defaultValue); - } - - // if field has a parent field, pass it on - if (parentFieldName) { - field.parentFieldName = parentFieldName; - } - field.label = this.getLabel(fieldName); - // // replace value by prefilled value if value is empty // const prefill = fieldSchema.prefill || (fieldSchema.form && fieldSchema.form.prefill); // if (prefill) { @@ -383,24 +403,65 @@ class SmartForm extends Component { field.options = field.options.call(fieldSchema, this.props); } - // add any properties specified in fieldSchema.form as extra props passed on - // to the form component, calling them if they are functions - const inputProperties = fieldSchema.form || fieldSchema.inputProperties || {}; - for (const prop in inputProperties) { - const property = inputProperties[prop]; - field[prop] = typeof property === 'function' ? property.call(fieldSchema, this.props) : property; + // if this an intl'd field, use a special intlInput + if (isIntlField(fieldSchema)) { + field.intlInput = true; } - // if field is not creatable/updatable, disable it - if (!this.getMutableFields(schema).includes(fieldName)) { - field.disabled = true; + // add any properties specified in fieldSchema.form as extra props passed on + // to the form component, calling them if they are functions + const inputProperties = + fieldSchema.form || fieldSchema.inputProperties || {}; + for (const prop in inputProperties) { + const property = inputProperties[prop]; + field[prop] = + typeof property === 'function' + ? property.call(fieldSchema, this.props) + : property; } // add description as help prop if (fieldSchema.description) { field.help = fieldSchema.description; } + return field; + }; + handleFieldPath = (field, fieldName, parentPath) => { + const fieldPath = parentPath ? `${parentPath}.${fieldName}` : fieldName; + field.path = fieldPath; + if (field.defaultValue) { + set(this.defaultValues, fieldPath, field.defaultValue); + } + return field; + }; + handleFieldParent = (field, parentFieldName) => { + // if field has a parent field, pass it on + if (parentFieldName) { + field.parentFieldName = parentFieldName; + } + return field; + }; + handlePermissions = (field, fieldName, schema) => { + // if field is not creatable/updatable, disable it + if (!this.getMutableFields(schema).includes(fieldName)) { + field.disabled = true; + } + return field; + }; + handleFieldChildren = (field, fieldName, fieldSchema, schema) => { + // array field + if (fieldSchema.field) { + field.arrayFieldSchema = fieldSchema.field; + // create a field that can be exploited by the form + field.arrayField = this.createArraySubField( + fieldName, + field.arrayFieldSchema, + schema + ); + + //field.nestedInput = true + } // nested fields: set input to "nested" if (fieldSchema.schema) { field.nestedSchema = fieldSchema.schema; @@ -408,21 +469,53 @@ class SmartForm extends Component { // get nested schema // for each nested field, get field object by calling createField recursively - field.nestedFields = this.getFieldNames({ schema: field.nestedSchema }).map(subFieldName => { - return this.createField(subFieldName, field.nestedSchema, fieldName, fieldPath); + field.nestedFields = this.getFieldNames({ + schema: field.nestedSchema + }).map(subFieldName => { + return this.createField( + subFieldName, + field.nestedSchema, + fieldName, + field.path + ); }); } - return field; }; /* + Given a field's name, the containing schema, and parent, create the + complete field object to be passed to the component + + */ + createField = (fieldName, schema, parentFieldName, parentPath) => { + const fieldSchema = schema[fieldName]; + let field = this.initField(fieldName, fieldSchema); + field = this.handleFieldPath(field, fieldName, parentPath); + field = this.handleFieldParent(field, parentFieldName); + field = this.handlePermissions(field, fieldName, schema); + field = this.handleFieldChildren(field, fieldName, fieldSchema, schema); + return field; + }; + createArraySubField = (fieldName, subFieldSchema, schema) => { + const subFieldName = `${fieldName}.$`; + let subField = this.initField(subFieldName, subFieldSchema); + // array subfield has the same path and permissions as its parent + // so we use parent name (fieldName) and not subfieldName + subField = this.handleFieldPath(subField, fieldName); + subField = this.handlePermissions(subField, fieldName, schema); + // we do not allow nesting yet + //subField = this.handleFieldChildren(field, fieldSchema) + return subField; + }; + /* + Get a field's label - + */ getLabel = (fieldName, fieldLocale) => { - const collectionName = this.getCollection().options.collectionName.toLowerCase(); + const collectionName = this.props.collectionName.toLowerCase(); const defaultMessage = '|*|*|'; let id = `${collectionName}.${fieldName}`; let intlLabel; @@ -435,10 +528,15 @@ class SmartForm extends Component { intlLabel = this.context.intl.formatMessage({ id }); } } - const schemaLabel = this.state.flatSchema[fieldName] && this.state.flatSchema[fieldName].label; + const schemaLabel = + this.state.flatSchema[fieldName] && + this.state.flatSchema[fieldName].label; const label = intlLabel || schemaLabel || fieldName; if (fieldLocale) { - const intlFieldLocale = this.context.intl.formatMessage({ id: `locales.${fieldLocale}`, defaultMessage: fieldLocale }); + const intlFieldLocale = this.context.intl.formatMessage({ + id: `locales.${fieldLocale}`, + defaultMessage: fieldLocale + }); return `${label} (${intlFieldLocale})`; } else { return label; @@ -450,9 +548,9 @@ class SmartForm extends Component { // --------------------------------------------------------------------- // /* - + Add error to form state - + Errors can have the following properties: - id: used as an internationalization key, for example `errors.required` - path: for field-specific errors, the path of the field with the issue @@ -468,14 +566,14 @@ class SmartForm extends Component { // add error(s) to state this.setState(prevState => ({ - errors: [...prevState.errors, ...formErrors], + errors: [...prevState.errors, ...formErrors] })); }; /* - + Clear errors for a field - + */ clearFieldErrors = path => { const errors = this.state.errors.filter(error => error.path !== path); @@ -489,7 +587,7 @@ class SmartForm extends Component { // add something to deleted values addToDeletedValues = name => { this.setState(prevState => ({ - deletedValues: [...prevState.deletedValues, name], + deletedValues: [...prevState.deletedValues, name] })); }; @@ -518,8 +616,8 @@ class SmartForm extends Component { prevState => ({ currentValues: { ...prevState.currentValues, - ...newValues, - }, // Submit form after setState update completed + ...newValues + } // Submit form after setState update completed }), () => this.submitForm(this.form.getModel()) ); @@ -546,7 +644,7 @@ class SmartForm extends Component { addToFailureForm: this.addToFailureForm, errors: this.state.errors, currentValues: this.state.currentValues, - deletedValues: this.state.deletedValues, + deletedValues: this.state.deletedValues }; }; @@ -555,35 +653,42 @@ class SmartForm extends Component { // --------------------------------------------------------------------- // /* - - When props change, reinitialize state - - // TODO: only need to check nextProps.prefilledProps? - // TODO: see https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html + When props change, reinitialize the form state + Triggered only for data related props (collection, document, currentUser etc.) + + @see https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html + */ UNSAFE_componentWillReceiveProps(nextProps) { - if (!isEqual(this.props, nextProps)) { + const needReset = !!RESET_PROPS.find(prop => !isEqual(this.props[prop], nextProps[prop])); + if (needReset) { this.setState(getInitialStateFromProps(nextProps)); } } /* - + Manually update the current values of one or more fields(i.e. on change or blur). - + */ updateCurrentValues = (newValues, options = {}) => { - // default to overwriting old value with new const { mode = 'overwrite' } = options; + const { changeCallback } = this.props; // keep the previous ones and extend (with possible replacement) with new ones this.setState(prevState => { - // keep only the relevant properties - const { currentValues, currentDocument, deletedValues } = cloneDeep(prevState); - const newState = { currentValues, currentDocument, deletedValues, foo: {} }; + const { currentValues, currentDocument, deletedValues } = cloneDeep( + prevState + ); + const newState = { + currentValues, + currentDocument, + deletedValues, + foo: {} + }; Object.keys(newValues).forEach(key => { const path = key; @@ -610,20 +715,21 @@ class SmartForm extends Component { newState.deletedValues = _.without(prevState.deletedValues, path); } }); + if (changeCallback) changeCallback(newState.currentDocument); return newState; }); }; /* - + Warn the user if there are unsaved changes - + */ handleRouteLeave = () => { if (this.isChanged()) { const message = this.context.intl.formatMessage({ id: 'forms.confirm_discard', - defaultMessage: 'Are you sure you want to discard your changes?', + defaultMessage: 'Are you sure you want to discard your changes?' }); return message; } @@ -631,7 +737,7 @@ class SmartForm extends Component { //see https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload //the message returned is actually ignored by most browsers and a default message 'Are you sure you want to leave this page? You might have unsaved changes' is displayed. See the Notes section on the mozilla docs above - handlePageLeave = (event) => { + handlePageLeave = event => { if (this.isChanged()) { const message = this.context.intl.formatMessage({ id: 'forms.confirm_discard', @@ -646,9 +752,9 @@ class SmartForm extends Component { }; /* - + Install a route leave hook to warn the user if there are unsaved changes - + */ componentDidMount = () => { let warnUnsavedChanges = getSetting('forms.warnUnsavedChanges'); @@ -680,9 +786,9 @@ class SmartForm extends Component { }; /* - + Returns true if there are any differences between the initial document and the current one - + */ isChanged = () => { const initialDocument = this.state.initialDocument; @@ -698,9 +804,9 @@ class SmartForm extends Component { }; /* - + Refetch the document from the database (in case it was updated by another process or to reset the form) - + */ refetchForm = () => { if (this.props.data && this.props.data.refetch) { @@ -708,31 +814,45 @@ class SmartForm extends Component { } }; - /* - - Clear and reset the form - By default, clear errors and keep current values and deleted values - - On form success we'll clear current values too. Note: document includes currentValues - - */ - clearForm = ({ clearErrors = true, clearCurrentValues = false, clearDeletedValues = false, document }) => { + /** + * Clears form errors and values. + * + * @example Clear form + * // form will be fully emptied, with exception of prefilled values + * clearForm({ document: {} }); + * + * @example Reset/revert form + * // form will be reverted to its initial state + * clearForm(); + * + * @example Clear with new values + * // form will be cleared but initialized with the new document + * const document = { + * // ... some values + * }; + * clearForm({ document }); + * + * @param {Object=} options + * @param {Object=} options.document + * Document to use as new initial document when values are cleared instead of + * the existing one. Note that prefilled props will be merged + */ + clearForm = ({ document } = {}) => { document = document ? merge({}, this.props.prefilledProps, document) : null; - this.setState(prevState => ({ - errors: clearErrors ? [] : prevState.errors, - currentValues: clearCurrentValues ? {} : prevState.currentValues, - currentDocument: clearCurrentValues ? {} : prevState.currentDocument, - deletedValues: clearDeletedValues ? [] : prevState.deletedValues, - initialDocument: document && !clearCurrentValues ? document : prevState.initialDocument, - disabled: false, + errors: [], + currentValues: {}, + deletedValues: [], + currentDocument: document || prevState.initialDocument, + initialDocument: document || prevState.initialDocument, + disabled: false })); }; /* - + Key down handler - + */ formKeyDown = event => { if ((event.ctrlKey || event.metaKey) && event.keyCode === 13) { @@ -749,9 +869,8 @@ class SmartForm extends Component { }; mutationSuccessCallback = (result, mutationType) => { - this.setState(prevState => ({ disabled: false })); - const document = result.data[Object.keys(result.data)[0]].data; // document is always on first property + let document = result.data[Object.keys(result.data)[0]].data; // document is always on first property // for new mutation, run refetch function if it exists if (mutationType === 'new' && this.props.refetch) this.props.refetch(); @@ -760,14 +879,16 @@ class SmartForm extends Component { // (we are in an async callback, everything can happen!) if (this.form) { this.form.reset(this.getDocument()); - this.clearForm({ clearErrors: true, clearCurrentValues: true, clearDeletedValues: true, document }); + this.clearForm({ + document: mutationType === 'edit' ? document : undefined + }); } // run document through mutation success callbacks - result = runCallbacks(this.successFormCallbacks, result); + document = runCallbacks({ callbacks: this.successFormCallbacks, iterator: document, properties: { form: this }}); // run success callback if it exists - if (this.props.successCallback) this.props.successCallback(document); + if (this.props.successCallback) this.props.successCallback(document, { form: this }); }; // catch graphql errors @@ -780,7 +901,7 @@ class SmartForm extends Component { console.log(error); // run mutation failure callbacks on error, we do not allow the callbacks to change the error - runCallbacks(this.failureFormCallbacks, error); + runCallbacks({ callbacks: this.failureFormCallbacks, iterator: error, properties: { error, form: this }}); if (!_.isEmpty(error)) { // add error to state @@ -788,16 +909,16 @@ class SmartForm extends Component { } // run error callback if it exists - if (this.props.errorCallback) this.props.errorCallback(document, error); + if (this.props.errorCallback) this.props.errorCallback(document, error, { form: this }); // scroll back up to show error messages Utils.scrollIntoView('.flash-message'); }; /* - + Submit form handler - + */ submitForm = data => { // note: we can discard the data collected by Formsy because all the data we need is already available via getDocument() @@ -821,22 +942,25 @@ class SmartForm extends Component { if (this.getFormType() === 'new') { // create document form - this.props[`create${this.getTypeName()}`]({ data }) + this.props[`create${this.props.typeName}`]({ data }) .then(this.newMutationSuccessCallback) .catch(error => this.mutationErrorCallback(document, error)); } else { // update document form const documentId = this.getDocument()._id; - this.props[`update${this.getTypeName()}`]({ selector: { documentId }, data }) + this.props[`update${this.props.typeName}`]({ + selector: { documentId }, + data + }) .then(this.editMutationSuccessCallback) .catch(error => this.mutationErrorCallback(document, error)); } }; /* - + Delete document handler - + */ deleteDocument = () => { const document = this.getDocument(); @@ -853,7 +977,8 @@ class SmartForm extends Component { .removeMutation({ documentId }) .then(mutationResult => { // the mutation result looks like {data:{collectionRemove: null}} if succeeded - if (this.props.removeSuccessCallback) this.props.removeSuccessCallback({ documentId, documentTitle }); + if (this.props.removeSuccessCallback) + this.props.removeSuccessCallback({ documentId, documentTitle }); if (this.props.refetch) this.props.refetch(); }) .catch(error => { @@ -869,15 +994,23 @@ class SmartForm extends Component { render() { const fieldGroups = this.getFieldGroups(); - const collectionName = this.getCollection()._name; + const collectionName = this.props.collectionName; + const FormComponents = mergeWithComponents(this.props.formComponents); return (
- { this.form = e; }}> - + { + this.form = e; + }} + > + {fieldGroups.map(group => ( - ))} {this.props.repeatErrors && this.renderErrors()} - { - if (!props.collection && !props.collectionName) { - return new Error(`One of props 'collection' or 'collectionName' was not specified in '${componentName}'.`); - } - if (!props.collection && typeof props['collectionName'] !== 'string') { - return new Error(`Prop collectionName was not of type string in '${componentName}`); - } - }, + collection: PropTypes.object.isRequired, + collectionName: PropTypes.string.isRequired, + typeName: PropTypes.string.isRequired, + document: PropTypes.object, // if a document is passed, this will be an edit form schema: PropTypes.object, // usually not needed @@ -941,33 +1075,29 @@ SmartForm.propTypes = { removeFields: PropTypes.arrayOf(PropTypes.string), hideFields: PropTypes.arrayOf(PropTypes.string), // OpenCRUD backwards compatibility showRemove: PropTypes.bool, - submitLabel: PropTypes.string, - cancelLabel: PropTypes.string, - revertLabel: PropTypes.string, + submitLabel: PropTypes.node, + cancelLabel: PropTypes.node, + revertLabel: PropTypes.node, repeatErrors: PropTypes.bool, warnUnsavedChanges: PropTypes.bool, + formComponents: PropTypes.object, // callbacks - submitCallback: PropTypes.func, - successCallback: PropTypes.func, - removeSuccessCallback: PropTypes.func, - errorCallback: PropTypes.func, - cancelCallback: PropTypes.func, - revertCallback: PropTypes.func, + ...callbackProps, currentUser: PropTypes.object, - client: PropTypes.object, + client: PropTypes.object }; SmartForm.defaultProps = { layout: 'horizontal', prefilledProps: {}, repeatErrors: false, - showRemove: true, + showRemove: true }; SmartForm.contextTypes = { - intl: intlShape, + intl: intlShape }; SmartForm.childContextTypes = { @@ -987,9 +1117,13 @@ SmartForm.childContextTypes = { getLabel: PropTypes.func, submitForm: PropTypes.func, errors: PropTypes.array, - currentValues: PropTypes.object, + currentValues: PropTypes.object }; module.exports = SmartForm; -registerComponent('Form', SmartForm); +registerComponent({ + name: 'Form', + component: SmartForm, + hocs: [withCollectionProps] +}); diff --git a/packages/vulcan-forms/lib/components/FormComponent.jsx b/packages/vulcan-forms/lib/components/FormComponent.jsx index 2d6bfd84c..26e428bb8 100644 --- a/packages/vulcan-forms/lib/components/FormComponent.jsx +++ b/packages/vulcan-forms/lib/components/FormComponent.jsx @@ -1,10 +1,10 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { Components } from 'meteor/vulcan:core'; -import { registerComponent } from 'meteor/vulcan:core'; +import { registerComponent, mergeWithComponents } from 'meteor/vulcan:core'; import get from 'lodash/get'; import isEqual from 'lodash/isEqual'; -import SimpleSchema from 'simpl-schema' +import SimpleSchema from 'simpl-schema'; import { isEmptyValue, getNullValue } from '../modules/utils.js'; class FormComponent extends Component { @@ -31,15 +31,24 @@ class FormComponent extends Component { const { path } = this.props; // when checking for deleted values, both current path ('foo') and child path ('foo.0.bar') should trigger updates - const includesPathOrChildren = deletedValues => deletedValues.some(deletedPath => deletedPath.includes(path)); + const includesPathOrChildren = deletedValues => + deletedValues.some(deletedPath => deletedPath.includes(path)); - const valueChanged = get(currentValues, path) !== get(this.props.currentValues, path); + const valueChanged = + get(currentValues, path) !== get(this.props.currentValues, path); const errorChanged = !isEqual(this.getErrors(errors), this.getErrors()); - const deleteChanged = includesPathOrChildren(deletedValues) !== includesPathOrChildren(this.props.deletedValues); + const deleteChanged = + includesPathOrChildren(deletedValues) !== + includesPathOrChildren(this.props.deletedValues); const charsChanged = nextState.charsRemaining !== this.state.charsRemaining; const disabledChanged = nextProps.disabled !== this.props.disabled; - const shouldUpdate = valueChanged || errorChanged || deleteChanged || charsChanged || disabledChanged; + const shouldUpdate = + valueChanged || + errorChanged || + deleteChanged || + charsChanged || + disabledChanged; return shouldUpdate; } @@ -74,7 +83,7 @@ class FormComponent extends Component { 'datetime', 'date', 'time', - 'text', + 'text' ].includes(inputType); return !isStandardInput; }; @@ -94,7 +103,9 @@ class FormComponent extends Component { value = Number(value); } - const updateValue = this.props.locale ? { locale: this.props.locale, value } : value; + const updateValue = this.props.locale + ? { locale: this.props.locale, value } + : value; this.props.updateCurrentValues({ [this.getPath()]: updateValue }); // for text fields, update character count on change @@ -112,7 +123,7 @@ class FormComponent extends Component { const characterCount = value ? value.length : 0; this.setState({ charsRemaining: this.props.max - characterCount, - charsCount: characterCount, + charsCount: characterCount }); }; @@ -148,7 +159,9 @@ class FormComponent extends Component { */ showCharsRemaining = props => { const p = props || this.props; - return p.max && ['url', 'email', 'textarea', 'text'].includes(this.getType(p)); + return ( + p.max && ['url', 'email', 'textarea', 'text'].includes(this.getType(p)) + ); }; /* @@ -160,7 +173,9 @@ class FormComponent extends Component { */ getErrors = errors => { errors = errors || this.props.errors; - const fieldErrors = errors.filter(error => error.path && error.path.includes(this.props.path)); + const fieldErrors = errors.filter( + error => error.path && error.path.includes(this.props.path) + ); return fieldErrors; }; @@ -173,7 +188,13 @@ class FormComponent extends Component { const p = props || this.props; const fieldType = p.datatype && p.datatype[0].type; const autoType = - fieldType === Number ? 'number' : fieldType === Boolean ? 'checkbox' : fieldType === Date ? 'date' : 'text'; + fieldType === Number + ? 'number' + : fieldType === Boolean + ? 'checkbox' + : fieldType === Date + ? 'date' + : 'text'; return p.input || autoType; }; @@ -198,6 +219,7 @@ class FormComponent extends Component { */ getFormInput = () => { const inputType = this.getType(); + const FormComponents = mergeWithComponents(this.props.formComponents); // if input is a React component, use it if (typeof this.props.input === 'function') { @@ -208,78 +230,96 @@ class FormComponent extends Component { switch (inputType) { case 'text': - return Components.FormComponentDefault; + return FormComponents.FormComponentDefault; case 'number': - return Components.FormComponentNumber; + return FormComponents.FormComponentNumber; case 'url': - return Components.FormComponentUrl; + return FormComponents.FormComponentUrl; case 'email': - return Components.FormComponentEmail; + return FormComponents.FormComponentEmail; case 'textarea': - return Components.FormComponentTextarea; + return FormComponents.FormComponentTextarea; case 'checkbox': - return Components.FormComponentCheckbox; + return FormComponents.FormComponentCheckbox; case 'checkboxgroup': - return Components.FormComponentCheckboxGroup; + return FormComponents.FormComponentCheckboxGroup; case 'radiogroup': - return Components.FormComponentRadioGroup; + return FormComponents.FormComponentRadioGroup; case 'select': - return Components.FormComponentSelect; + return FormComponents.FormComponentSelect; case 'selectmultiple': - return Components.FormComponentSelectMultiple; + return FormComponents.FormComponentSelectMultiple; case 'datetime': - return Components.FormComponentDateTime; + return FormComponents.FormComponentDateTime; case 'date': - return Components.FormComponentDate; + return FormComponents.FormComponentDate; case 'date2': - return Components.FormComponentDate2; + return FormComponents.FormComponentDate2; case 'time': - return Components.FormComponentTime; + return FormComponents.FormComponentTime; case 'statictext': - return Components.FormComponentStaticText; + return FormComponents.FormComponentStaticText; default: - const CustomComponent = Components[this.props.input]; - return CustomComponent ? CustomComponent : Components.FormComponentDefault; + const CustomComponent = FormComponents[this.props.input]; + return CustomComponent + ? CustomComponent + : FormComponents.FormComponentDefault; } } }; getFieldType = () => { - return this.props.datatype[0].type - } + return this.props.datatype[0].type; + }; isArrayField = () => { - return this.getFieldType() === Array - } + return this.getFieldType() === Array; + }; isObjectField = () => { - return this.getFieldType() instanceof SimpleSchema - } + return this.getFieldType() instanceof SimpleSchema; + }; render() { + const FormComponents = mergeWithComponents(this.props.formComponents); + if (this.props.intlInput) { - return ; - } else if (this.props.nestedInput) { + return ; + } else if (!this.props.input && this.props.nestedInput) { if (this.isArrayField()) { - return ; + return ( + + ); } else if (this.isObjectField()) { - return ; + return ( + + ); } } return ( - ); } @@ -315,13 +356,13 @@ FormComponent.propTypes = { errors: PropTypes.array.isRequired, addToDeletedValues: PropTypes.func, clearFieldErrors: PropTypes.func.isRequired, - currentUser: PropTypes.object, + currentUser: PropTypes.object }; FormComponent.contextTypes = { - getDocument: PropTypes.func.isRequired, + getDocument: PropTypes.func.isRequired }; -module.exports = FormComponent +module.exports = FormComponent; registerComponent('FormComponent', FormComponent); diff --git a/packages/vulcan-forms/lib/components/FormError.jsx b/packages/vulcan-forms/lib/components/FormError.jsx index 725ae535e..42ec4b1c4 100644 --- a/packages/vulcan-forms/lib/components/FormError.jsx +++ b/packages/vulcan-forms/lib/components/FormError.jsx @@ -8,19 +8,21 @@ const FormError = ({ error, errorContext, getLabel }) => { if (error.message) { return error.message; } + // in case this is a nested fields, only keep last segment of path + const errorName = error.properties.name.split('.').slice(-1)[0]; return ( ) -;} +;}; FormError.defaultProps = { errorContext: '', // default context so format message does not complain diff --git a/packages/vulcan-forms/lib/components/FormGroup.jsx b/packages/vulcan-forms/lib/components/FormGroup.jsx index 7eabfbc4b..2f28ca526 100644 --- a/packages/vulcan-forms/lib/components/FormGroup.jsx +++ b/packages/vulcan-forms/lib/components/FormGroup.jsx @@ -1,8 +1,47 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; -import { Components } from 'meteor/vulcan:core'; +import { Components, Utils } from 'meteor/vulcan:core'; import classNames from 'classnames'; -import { registerComponent } from 'meteor/vulcan:core'; +import { registerComponent, mergeWithComponents } from 'meteor/vulcan:core'; + +const FormGroupHeader = ({ toggle, collapsed, label }) => ( +
+

{label}

+ + {collapsed ? ( + + ) : ( + + )} + +
+); +FormGroupHeader.propTypes = { + toggle: PropTypes.func.isRequired, + label: PropTypes.string.isRequired, + collapsed: PropTypes.bool +}; +registerComponent({ name: 'FormGroupHeader', component: FormGroupHeader }); + +const FormGroupLayout = ({ children, label, heading, collapsed, hasErrors }) => ( +
+ {heading} +
+ {children} +
+
+); +FormGroupLayout.propTypes = { + hasErrors: PropTypes.bool, + collapsed: PropTypes.bool, + heading: PropTypes.node, + children: PropTypes.node +}; +registerComponent({ name: 'FormGroupLayout', component: FormGroupLayout }); class FormGroup extends PureComponent { constructor(props) { @@ -10,56 +49,64 @@ class FormGroup extends PureComponent { this.toggle = this.toggle.bind(this); this.renderHeading = this.renderHeading.bind(this); this.state = { - collapsed: props.startCollapsed || false, + collapsed: props.startCollapsed || false }; } toggle() { this.setState({ - collapsed: !this.state.collapsed, + collapsed: !this.state.collapsed }); } - renderHeading() { + renderHeading(FormComponents) { return ( -
-

{this.props.label}

- - {this.state.collapsed ? : } - -
+ ); } // if at least one of the fields in the group has an error, the group as a whole has an error - hasErrors = () => _.some(this.props.fields, field => { - return !!this.props.errors.filter(error => error.path === field.path).length - }); + hasErrors = () => + _.some(this.props.fields, field => { + return !!this.props.errors.filter(error => error.path === field.path) + .length; + }); render() { + const { name, fields, formComponents, label } = this.props; + const { collapsed } = this.state; + const FormComponents = mergeWithComponents(formComponents); return ( -
- {this.props.name === 'default' ? null : this.renderHeading()} -
- {this.props.fields.map(field => ( - - ))} -
-
+ + {fields.map(field => ( + + ))} + ); } } @@ -77,10 +124,10 @@ FormGroup.propTypes = { addToDeletedValues: PropTypes.func.isRequired, clearFieldErrors: PropTypes.func.isRequired, formType: PropTypes.string.isRequired, - currentUser: PropTypes.object, + currentUser: PropTypes.object }; -module.exports = FormGroup +module.exports = FormGroup; registerComponent('FormGroup', FormGroup); diff --git a/packages/vulcan-forms/lib/components/FormIntl.jsx b/packages/vulcan-forms/lib/components/FormIntl.jsx index 4a37b1896..782e61b30 100644 --- a/packages/vulcan-forms/lib/components/FormIntl.jsx +++ b/packages/vulcan-forms/lib/components/FormIntl.jsx @@ -1,11 +1,25 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; -import { Components, registerComponent, Locales } from 'meteor/vulcan:core'; +import { Components, registerComponent, mergeWithComponents, Locales } from 'meteor/vulcan:core'; import omit from 'lodash/omit'; import getContext from 'recompose/getContext'; -class FormIntl extends PureComponent { +// replaceable layout +const FormIntlLayout = ({ children }) => ( +
{children}
+); +registerComponent({ name: 'FormIntlLayout', component: FormIntlLayout }); +const FormIntlItemLayout = ({ locale, children }) => ( +
+ {children} +
+); +registerComponent({ + name: 'FormIntlItemLayout', + component: FormIntlItemLayout +}); +class FormIntl extends PureComponent { /* Note: ideally we'd try to make sure to return the right path no matter @@ -13,27 +27,49 @@ class FormIntl extends PureComponent { so we just use the order of the Locales array. */ - getLocalePath = (defaultIndex) => { + getLocalePath = defaultIndex => { return `${this.props.path}_intl.${defaultIndex}`; - } - + }; + render() { + const { name, formComponents } = this.props; + const FormComponents = mergeWithComponents(formComponents); // do not pass FormIntl's own value, inputProperties, and intlInput props down - const properties = omit(this.props, 'value', 'inputProperties', 'intlInput', 'nestedInput'); - + const properties = omit( + this.props, + 'value', + 'inputProperties', + 'intlInput', + 'nestedInput' + ); return ( -
+ {Locales.map((locale, i) => ( -
- -
+ + + ))} -
+ ); } } -registerComponent('FormIntl', FormIntl, getContext({ - getLabel: PropTypes.func, -})); +FormIntl.propTypes = { + name: PropTypes.string.isRequired, + path: PropTypes.string.isRequired, + formComponents: PropTypes.object +}; + +registerComponent( + 'FormIntl', + FormIntl, + getContext({ + getLabel: PropTypes.func + }) +); diff --git a/packages/vulcan-forms/lib/components/FormNestedArray.jsx b/packages/vulcan-forms/lib/components/FormNestedArray.jsx index e927fa676..96ab98528 100644 --- a/packages/vulcan-forms/lib/components/FormNestedArray.jsx +++ b/packages/vulcan-forms/lib/components/FormNestedArray.jsx @@ -2,14 +2,36 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { Components, registerComponent } from 'meteor/vulcan:core'; +// Replaceable layout +const FormNestedArrayLayout = ({ hasErrors, label, content }) => ( +
+ +
{content}
+
+); +FormNestedArrayLayout.propTypes = { + hasErrors: PropTypes.bool, + label: PropTypes.node, + content: PropTypes.node +}; +registerComponent({ + name: 'FormNestedArrayLayout', + component: FormNestedArrayLayout +}); + class FormNestedArray extends PureComponent { getCurrentValue() { - return this.props.value || [] + return this.props.value || []; } addItem = () => { - const value = this.getCurrentValue() - this.props.updateCurrentValues({ [`${this.props.path}.${value.length}`]: {} }, { mode: 'merge'}); + const value = this.getCurrentValue(); + this.props.updateCurrentValues( + { [`${this.props.path}.${value.length}`]: {} }, + { mode: 'merge' } + ); }; removeItem = index => { @@ -27,40 +49,72 @@ class FormNestedArray extends PureComponent { }; render() { - const value = this.getCurrentValue() + const value = this.getCurrentValue(); // do not pass FormNested's own value, input and inputProperties props down - const properties = _.omit(this.props, 'value', 'input', 'inputProperties', 'nestedInput'); - const { errors, path } = this.props; - // only keep errors specific to the nested array (and not its subfields) - const nestedArrayErrors = errors.filter(error => error.path && error.path === path); - const hasErrors = nestedArrayErrors && nestedArrayErrors.length; + const properties = _.omit( + this.props, + 'value', + 'input', + 'inputProperties', + 'nestedInput' + ); + const { errors, path, label, formComponents, minCount, maxCount } = this.props; + const FormComponents = formComponents; + //filter out null values to calculate array length + let arrayLength = value.filter(singleValue => { + return !!singleValue; + }).length; + + // only keep errors specific to the nested array (and not its subfields) + const nestedArrayErrors = errors.filter( + error => error.path && error.path === path + ); + const hasErrors = nestedArrayErrors && nestedArrayErrors.length; + return ( -
- -
- {value.map( + !this.isDeleted(i) && ( - { this.removeItem(i); }} + hideRemove={minCount && arrayLength <= minCount} + /> + - ) - )} - + ), + (!maxCount || arrayLength < maxCount) && ( + - {hasErrors ? : null} -
-
+ ), + hasErrors ? ( + + ) : null + ]} + /> ); } } @@ -68,15 +122,23 @@ class FormNestedArray extends PureComponent { FormNestedArray.propTypes = { currentValues: PropTypes.object, path: PropTypes.string, - label: PropTypes.string + label: PropTypes.string, + errors: PropTypes.array.isRequired, + deletedValues: PropTypes.array.isRequired, + formComponents: PropTypes.object.isRequired }; -module.exports = FormNestedArray +module.exports = FormNestedArray; registerComponent('FormNestedArray', FormNestedArray); const IconAdd = ({ width = 24, height = 24 }) => ( - + ); @@ -84,7 +146,12 @@ const IconAdd = ({ width = 24, height = 24 }) => ( registerComponent('IconAdd', IconAdd); const IconRemove = ({ width = 24, height = 24 }) => ( - + ); diff --git a/packages/vulcan-forms/lib/components/FormNestedDivider.jsx b/packages/vulcan-forms/lib/components/FormNestedDivider.jsx index 465293875..aceb25d22 100644 --- a/packages/vulcan-forms/lib/components/FormNestedDivider.jsx +++ b/packages/vulcan-forms/lib/components/FormNestedDivider.jsx @@ -1,7 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import { registerComponent } from 'meteor/vulcan:core'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; const FormNestedDivider = ({ label, addItem }) =>
; diff --git a/packages/vulcan-forms/lib/components/FormNestedFoot.jsx b/packages/vulcan-forms/lib/components/FormNestedFoot.jsx index 379f4adb1..4a3fa9914 100644 --- a/packages/vulcan-forms/lib/components/FormNestedFoot.jsx +++ b/packages/vulcan-forms/lib/components/FormNestedFoot.jsx @@ -1,7 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Components, registerComponent } from 'meteor/vulcan:core'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; const FormNestedFoot = ({ label, addItem }) => ( diff --git a/packages/vulcan-forms/lib/components/FormNestedHead.jsx b/packages/vulcan-forms/lib/components/FormNestedHead.jsx index 88b77ca3c..aa4f338fd 100644 --- a/packages/vulcan-forms/lib/components/FormNestedHead.jsx +++ b/packages/vulcan-forms/lib/components/FormNestedHead.jsx @@ -1,7 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import { registerComponent } from 'meteor/vulcan:core'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; const FormNestedHead = ({ label, addItem }) => ( diff --git a/packages/vulcan-forms/lib/components/FormNestedItem.jsx b/packages/vulcan-forms/lib/components/FormNestedItem.jsx index 6871a25ca..90c348f2d 100644 --- a/packages/vulcan-forms/lib/components/FormNestedItem.jsx +++ b/packages/vulcan-forms/lib/components/FormNestedItem.jsx @@ -1,52 +1,83 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Components, registerComponent } from 'meteor/vulcan:core'; +import { Components, registerComponent, mergeWithComponents } from 'meteor/vulcan:core'; -const FormNestedItem = ({ nestedFields, name, path, removeItem, itemIndex, ...props }, { errors }) => { +const FormNestedItemLayout = ({ content, removeButton }) => ( +
+
{content}
+ {removeButton && [ +
+ {removeButton} +
, +
+ ]} +
+); +FormNestedItemLayout.propTypes = { + content: PropTypes.node.isRequired, + removeButton: PropTypes.node +}; +registerComponent({ + name: 'FormNestedItemLayout', + component: FormNestedItemLayout +}); + +const FormNestedItem = ( + { nestedFields, name, path, removeItem, itemIndex, formComponents, hideRemove, ...props }, + { errors } +) => { + const FormComponents = mergeWithComponents(formComponents); const isArray = typeof itemIndex !== 'undefined'; return ( -
-
- {nestedFields.map((field, i) => { - return ( - - ); - })} -
- {isArray && [ -
- { - removeItem(name); - }} - > - - -
, -
, - ]} -
+ { + return ( + + ); + })} + removeButton={ + isArray && !hideRemove && [ +
+ { + removeItem(name); + }} + > + + +
, +
+ ] + } + /> ); }; FormNestedItem.propTypes = { path: PropTypes.string.isRequired, itemIndex: PropTypes.number, + formComponents: PropTypes.object }; FormNestedItem.contextTypes = { - errors: PropTypes.array, + errors: PropTypes.array }; registerComponent('FormNestedItem', FormNestedItem); diff --git a/packages/vulcan-forms/lib/components/FormNestedObject.jsx b/packages/vulcan-forms/lib/components/FormNestedObject.jsx index 08b246643..98205a3a9 100644 --- a/packages/vulcan-forms/lib/components/FormNestedObject.jsx +++ b/packages/vulcan-forms/lib/components/FormNestedObject.jsx @@ -1,24 +1,62 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; -import { Components, registerComponent } from 'meteor/vulcan:core'; +import { registerComponent, mergeWithComponents } from 'meteor/vulcan:core'; + +// Replaceable layout +const FormNestedObjectLayout = ({ hasErrors, label, content }) => ( +
+ +
{content}
+
+); +FormNestedObjectLayout.propTypes = { + hasErrors: PropTypes.bool, + label: PropTypes.node, + content: PropTypes.node +}; +registerComponent({ + name: 'FormNestedObjectLayout', + component: FormNestedObjectLayout +}); class FormNestedObject extends PureComponent { render() { + const FormComponents = mergeWithComponents(this.props.formComponents); //const value = this.getCurrentValue() // do not pass FormNested's own value, input and inputProperties props down - const properties = _.omit(this.props, 'value', 'input', 'inputProperties', 'nestedInput'); + const properties = _.omit( + this.props, + 'value', + 'input', + 'inputProperties', + 'nestedInput' + ); const { errors } = this.props; // only keep errors specific to the nested array (and not its subfields) - const nestedObjectErrors = errors.filter(error => error.path && error.path === this.props.path); + const nestedObjectErrors = errors.filter( + error => error.path && error.path === this.props.path + ); const hasErrors = nestedObjectErrors && nestedObjectErrors.length; return ( -
- -
- - {hasErrors ? : null} -
-
+ , + hasErrors ? ( + + ) : null + ]} + /> ); } } @@ -27,6 +65,8 @@ FormNestedObject.propTypes = { currentValues: PropTypes.object, path: PropTypes.string, label: PropTypes.string, + errors: PropTypes.array.isRequired, + formComponents: PropTypes.object }; module.exports = FormNestedObject; diff --git a/packages/vulcan-forms/lib/components/FormSubmit.jsx b/packages/vulcan-forms/lib/components/FormSubmit.jsx index f7be2d7e4..6bf3586dd 100644 --- a/packages/vulcan-forms/lib/components/FormSubmit.jsx +++ b/packages/vulcan-forms/lib/components/FormSubmit.jsx @@ -40,7 +40,7 @@ const FormSubmit = ({ className="form-cancel" onClick={e => { e.preventDefault(); - clearForm({ clearErrors: true, clearCurrentValues: true, clearDeletedValues: true }); + clearForm(); revertCallback(document); }} > @@ -60,10 +60,10 @@ const FormSubmit = ({ ); FormSubmit.propTypes = { - submitLabel: PropTypes.string, - cancelLabel: PropTypes.string, + submitLabel: PropTypes.node, + cancelLabel: PropTypes.node, cancelCallback: PropTypes.func, - revertLabel: PropTypes.string, + revertLabel: PropTypes.node, revertCallback: PropTypes.func, document: PropTypes.object, deleteDocument: PropTypes.func, diff --git a/packages/vulcan-forms/lib/components/FormWrapper.jsx b/packages/vulcan-forms/lib/components/FormWrapper.jsx index 1f3dc610c..936f9f5a1 100644 --- a/packages/vulcan-forms/lib/components/FormWrapper.jsx +++ b/packages/vulcan-forms/lib/components/FormWrapper.jsx @@ -27,7 +27,7 @@ component is also added to wait for withSingle's loading prop to be false) import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { intlShape } from 'meteor/vulcan:i18n'; -import { withRouter } from 'react-router' +import { withRouter } from 'react-router'; import { withApollo, compose } from 'react-apollo'; import { Components, @@ -37,29 +37,32 @@ import { withNew, withUpdate, withDelete, - getFragment, - getCollection, + getFragment } from 'meteor/vulcan:core'; import gql from 'graphql-tag'; import { withSingle } from 'meteor/vulcan:core'; import { graphql } from 'react-apollo'; +import { + getReadableFields, + getCreateableFields, + getUpdateableFields +} from '../modules/schema_utils'; + +import withCollectionProps from './withCollectionProps'; +import { callbackProps } from './propTypes'; class FormWrapper extends PureComponent { - constructor(props) { super(props); // instantiate the wrapped component in constructor, not in render // see https://reactjs.org/docs/higher-order-components.html#dont-use-hocs-inside-the-render-method this.FormComponent = this.getComponent(props); } - - getCollection() { - return this.props.collection || getCollection(this.props.collectionName); - } - // return the current schema based on either the schema or collection prop getSchema() { - return this.props.schema ? this.props.schema : this.getCollection().simpleSchema()._schema; + return this.props.schema + ? this.props.schema + : this.props.collection.simpleSchema()._schema; } // if a document is being passed, this is an edit form @@ -67,71 +70,59 @@ class FormWrapper extends PureComponent { return this.props.documentId || this.props.slug ? 'edit' : 'new'; } - // filter out fields with "." or "$" - getValidFields() { - return Object.keys(this.getSchema()).filter(fieldName => !fieldName.includes('$') && !fieldName.includes('.')); - } - - getReadableFields() { - const schema = this.getSchema(); - // OpenCRUD backwards compatibility - return this.getValidFields().filter(fieldName => schema[fieldName].canRead || schema[fieldName].viewableBy); - } - - getCreateableFields() { - const schema = this.getSchema(); - // OpenCRUD backwards compatibility - return this.getValidFields().filter(fieldName => schema[fieldName].canCreate || schema[fieldName].insertableBy); - } - - getUpdatetableFields() { - const schema = this.getSchema(); - // OpenCRUD backwards compatibility - return this.getValidFields().filter(fieldName => schema[fieldName].canUpdate || schema[fieldName].editableBy); - } - // get fragment used to decide what data to load from the server to populate the form, // as well as what data to ask for as return value for the mutation getFragments() { - - const prefix = `${this.getCollection()._name}${Utils.capitalize(this.getFormType())}` + const prefix = `${this.props.collectionName}${Utils.capitalize( + this.getFormType() + )}`; const fragmentName = `${prefix}FormFragment`; const fields = this.props.fields; - const readableFields = this.getReadableFields(); - const createableFields = this.getCreateableFields(); - const updatetableFields = this.getUpdatetableFields(); + const readableFields = getReadableFields(this.getSchema()); + const createableFields = getCreateableFields(this.getSchema()); + const updatetableFields = getUpdateableFields(this.getSchema()); // get all editable/insertable fields (depending on current form type) - let queryFields = this.getFormType() === 'new' ? createableFields : updatetableFields; + let queryFields = + this.getFormType() === 'new' ? createableFields : updatetableFields; // for the mutations's return value, also get non-editable but viewable fields (such as createdAt, userId, etc.) - let mutationFields = this.getFormType() === 'new' ? _.unique(createableFields.concat(readableFields)) : _.unique(createableFields.concat(updatetableFields)); + let mutationFields = + this.getFormType() === 'new' + ? _.unique(createableFields.concat(readableFields)) + : _.unique(createableFields.concat(updatetableFields)); // if "fields" prop is specified, restrict list of fields to it if (typeof fields !== 'undefined' && fields.length > 0) { queryFields = _.intersection(queryFields, fields); mutationFields = _.intersection(mutationFields, fields); } - - const convertFields = field => { - return field.slice(-5) === '_intl' ? `${field}{ locale value }`: field; + + // add "addFields" prop contents to list of fields + if (this.props.addFields && this.props.addFields.length) { + queryFields = queryFields.concat(this.props.addFields); + mutationFields = mutationFields.concat(this.props.addFields); } + const convertFields = field => { + return field.slice(-5) === '_intl' ? `${field}{ locale value }` : field; + }; + // generate query fragment based on the fields that can be edited. Note: always add _id. const generatedQueryFragment = gql` - fragment ${fragmentName} on ${this.getCollection().typeName} { + fragment ${fragmentName} on ${this.props.typeName} { _id ${queryFields.map(convertFields).join('\n')} } - ` + `; // generate mutation fragment based on the fields that can be edited and/or viewed. Note: always add _id. const generatedMutationFragment = gql` - fragment ${fragmentName} on ${this.getCollection().typeName} { + fragment ${fragmentName} on ${this.props.typeName} { _id ${mutationFields.map(convertFields).join('\n')} } - ` + `; // default to generated fragments let queryFragment = generatedQueryFragment; @@ -139,10 +130,20 @@ class FormWrapper extends PureComponent { // if queryFragment or mutationFragment props are specified, accept either fragment object or fragment string if (this.props.queryFragment) { - queryFragment = typeof this.props.queryFragment === 'string' ? gql`${this.props.queryFragment}` : this.props.queryFragment; + queryFragment = + typeof this.props.queryFragment === 'string' + ? gql` + ${this.props.queryFragment} + ` + : this.props.queryFragment; } if (this.props.mutationFragment) { - mutationFragment = typeof this.props.mutationFragment === 'string' ? gql`${this.props.mutationFragment}` : this.props.mutationFragment; + mutationFragment = + typeof this.props.mutationFragment === 'string' + ? gql` + ${this.props.mutationFragment} + ` + : this.props.mutationFragment; } // same with queryFragmentName and mutationFragmentName @@ -154,108 +155,119 @@ class FormWrapper extends PureComponent { } // if any field specifies extra queries, add them - const extraQueries = _.compact(queryFields.map(fieldName => { - const field = this.getSchema()[fieldName]; - return field.query - })); + const extraQueries = _.compact( + queryFields.map(fieldName => { + const field = this.getSchema()[fieldName]; + return field.query; + }) + ); // get query & mutation fragments from props or else default to same as generatedFragment return { queryFragment, mutationFragment, - extraQueries, + extraQueries }; } getComponent() { - let WrappedComponent; - const prefix = `${this.getCollection()._name}${Utils.capitalize(this.getFormType())}` + const prefix = `${this.props.collectionName}${Utils.capitalize( + this.getFormType() + )}`; - const { queryFragment, mutationFragment, extraQueries } = this.getFragments(); + const { + queryFragment, + mutationFragment, + extraQueries + } = this.getFragments(); // props to pass on to child component (i.e.
) const childProps = { formType: this.getFormType(), - schema: this.getSchema(), + schema: this.getSchema() }; // options for withSingle HoC const queryOptions = { queryName: `${prefix}FormQuery`, - collection: this.getCollection(), + collection: this.props.collection, fragment: queryFragment, extraQueries, fetchPolicy: 'network-only', // we always want to load a fresh copy of the document enableCache: false, - pollInterval: 0, // no polling, only load data once + pollInterval: 0 // no polling, only load data once }; // options for withNew, withUpdate, and withDelete HoCs const mutationOptions = { - collection: this.getCollection(), - fragment: mutationFragment, + collection: this.props.collection, + fragment: mutationFragment }; // create a stateless loader component, // displays the loading state if needed, and passes on loading and document/data const Loader = props => { const { document, loading } = props; - return loading ? - : + return loading ? ( + + ) : ( ; + /> + ); }; Loader.displayName = 'withLoader(Form)'; // if this is an edit from, load the necessary data using the withSingle HoC if (this.getFormType() === 'edit') { - WrappedComponent = compose( withSingle(queryOptions), withUpdate(mutationOptions), withDelete(mutationOptions) )(Loader); - return - + return ( + + ); } else { - if (extraQueries && extraQueries.length) { - - const extraQueriesHoC = graphql(gql` + const extraQueriesHoC = graphql( + gql` query formNewExtraQuery { ${extraQueries} - }`, { + }`, + { alias: 'withExtraQueries', props: returnedProps => { const { /* ownProps, */ data } = returnedProps; const props = { loading: data.loading, - data, + data }; return props; - }, - }); + } + } + ); WrappedComponent = compose( extraQueriesHoC, withNew(mutationOptions) )(Loader); - } else { - WrappedComponent = compose( - withNew(mutationOptions) - )(Components.Form); + WrappedComponent = compose(withNew(mutationOptions))(Components.Form); } return ; - } } @@ -268,15 +280,10 @@ class FormWrapper extends PureComponent { FormWrapper.propTypes = { // main options - collection: PropTypes.object, - collectionName: (props, propName, componentName) => { - if (!props.collection && !props.collectionName) { - return new Error(`One of props 'collection' or 'collectionName' was not specified in '${componentName}'.`); - } - if (!props.collection && typeof props['collectionName'] !== 'string') { - return new Error(`Prop collectionName was not of type string in '${componentName}`); - } - }, + collection: PropTypes.object.isRequired, + collectionName: PropTypes.string.isRequired, + typeName: PropTypes.string.isRequired, + documentId: PropTypes.string, // if a document is passed, this will be an edit form schema: PropTypes.object, // usually not needed queryFragment: PropTypes.object, @@ -294,32 +301,32 @@ FormWrapper.propTypes = { layout: PropTypes.string, fields: PropTypes.arrayOf(PropTypes.string), hideFields: PropTypes.arrayOf(PropTypes.string), + addFields: PropTypes.arrayOf(PropTypes.string), showRemove: PropTypes.bool, - submitLabel: PropTypes.string, - cancelLabel: PropTypes.string, - revertLabel: PropTypes.string, + submitLabel: PropTypes.node, + cancelLabel: PropTypes.node, + revertLabel: PropTypes.node, repeatErrors: PropTypes.bool, warnUnsavedChanges: PropTypes.bool, // callbacks - submitCallback: PropTypes.func, - successCallback: PropTypes.func, - removeSuccessCallback: PropTypes.func, - errorCallback: PropTypes.func, - cancelCallback: PropTypes.func, - revertCallback: PropTypes.func, + ...callbackProps, currentUser: PropTypes.object, - client: PropTypes.object, -} + client: PropTypes.object +}; FormWrapper.defaultProps = { - layout: 'horizontal', -} + layout: 'horizontal' +}; FormWrapper.contextTypes = { closeCallback: PropTypes.func, intl: intlShape -} +}; -registerComponent('SmartForm', FormWrapper, withCurrentUser, withApollo, withRouter); +registerComponent({ + name: 'SmartForm', + component: FormWrapper, + hocs: [withCurrentUser, withApollo, withRouter, withCollectionProps] +}); diff --git a/packages/vulcan-forms/lib/components/propTypes.js b/packages/vulcan-forms/lib/components/propTypes.js new file mode 100644 index 000000000..1cdd329fc --- /dev/null +++ b/packages/vulcan-forms/lib/components/propTypes.js @@ -0,0 +1,47 @@ +/** PropTypes for documentation purpose (not tested yet) */ +import PropTypes from 'prop-types'; + +export const fieldProps = { + // + defaultValue: PropTypes.any, + help: PropTypes.string, + description: PropTypes.string, + // initial fields + name: PropTypes.string, + datatype: PropTypes.any, // ? + layout: PropTypes.any, // string? + input: PropTypes.any, // string, function, undefined + options: PropTypes.object, + intlInput: PropTypes.object, + // path relative to the main object + // e.g phoneNumbers.0.value + path: PropTypes.string, + // permissions + disabled: PropTypes.boolean, + // if it has an array field + // e.g addresses.$ : { type: .... } + arrayFieldSchema: PropTypes.object, + arrayField: PropTypes.object, //fieldProps, + // if it is a nested object itself + // eg address : { type : { ... }} + nestedSchema: PropTypes.object, + nestedInput: PropTypes.boolean, // flag + nestedFields: PropTypes.array //arrayOf(fieldProps) +}; + +export const groupProps = { + name: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + order: PropTypes.number, + fields: PropTypes.arrayOf(PropTypes.shape(fieldProps)) +}; + +export const callbackProps = { + changeCallback: PropTypes.func, + submitCallback: PropTypes.func, + successCallback: PropTypes.func, + removeSuccessCallback: PropTypes.func, + errorCallback: PropTypes.func, + cancelCallback: PropTypes.func, + revertCallback: PropTypes.func +}; diff --git a/packages/vulcan-forms/lib/components/withCollectionProps.js b/packages/vulcan-forms/lib/components/withCollectionProps.js new file mode 100644 index 000000000..ba9917d41 --- /dev/null +++ b/packages/vulcan-forms/lib/components/withCollectionProps.js @@ -0,0 +1,31 @@ +import React from 'react'; +import { extractCollectionInfo } from 'meteor/vulcan:lib'; +import PropTypes from 'prop-types'; + +/** + * Handle the collection or collectionName and pass down other related + * props (typeName, collectionName, etc.) + */ +const withCollectionProps = C => { + const CollectionPropsWrapper = ({ collection: _collection, collectionName: _collectionName, ...otherProps }) => { + const { collection, collectionName } = extractCollectionInfo({ + collection: _collection, + collectionName: _collectionName + }); + const typeName = collection.options.typeName; + return ; + }; + CollectionPropsWrapper.propTypes = { + collection: PropTypes.object, + collectionName: (props, propName, componentName) => { + if (!props.collection && !props.collectionName) { + return new Error(`One of props 'collection' or 'collectionName' was not specified in '${componentName}'.`); + } + if (!props.collection && typeof props['collectionName'] !== 'string') { + return new Error(`Prop collectionName was not of type string in '${componentName}`); + } + } + }; + return CollectionPropsWrapper; +}; +export default withCollectionProps; diff --git a/packages/vulcan-forms/lib/modules/schema_utils.js b/packages/vulcan-forms/lib/modules/schema_utils.js index 1e97cea89..f938eefc6 100644 --- a/packages/vulcan-forms/lib/modules/schema_utils.js +++ b/packages/vulcan-forms/lib/modules/schema_utils.js @@ -1,3 +1,59 @@ +/* + * Schema converter/getters + */ +import Users from 'meteor/vulcan:users'; +import _filter from 'lodash/filter'; +import _keys from 'lodash/keys'; + +/* getters */ +// filter out fields with "." or "$" +export const getValidFields = schema => { + return Object.keys(schema).filter(fieldName => !fieldName.includes('$') && !fieldName.includes('.')); +}; + +export const getReadableFields = schema => { + // OpenCRUD backwards compatibility + return getValidFields(schema).filter(fieldName => schema[fieldName].canRead || schema[fieldName].viewableBy); +}; + +export const getCreateableFields = schema => { + // OpenCRUD backwards compatibility + return getValidFields(schema).filter(fieldName => schema[fieldName].canCreate || schema[fieldName].insertableBy); +}; + +export const getUpdateableFields = schema => { + // OpenCRUD backwards compatibility + return getValidFields(schema).filter(fieldName => schema[fieldName].canUpdate || schema[fieldName].editableBy); +}; + +/* permissions */ + +/** + * @method Mongo.Collection.getInsertableFields + * Get an array of all fields editable by a specific user for a given collection + * @param {Object} user – the user for which to check field permissions + */ +export const getInsertableFields = function(schema, user) { + const fields = _filter(_keys(schema), function(fieldName) { + var field = schema[fieldName]; + return Users.canCreateField(user, field); + }); + return fields; +}; + +/** + * @method Mongo.Collection.getEditableFields + * Get an array of all fields editable by a specific user for a given collection (and optionally document) + * @param {Object} user – the user for which to check field permissions + */ +export const getEditableFields = function(schema, user, document) { + const fields = _.filter(_.keys(schema), function(fieldName) { + var field = schema[fieldName]; + return Users.canUpdateField(user, field, document); + }); + return fields; +}; + /* Convert a nested SimpleSchema schema into a JSON object @@ -17,15 +73,24 @@ export const convertSchema = (schema, flatten = false) => { // extract schema jsonSchema[fieldName] = getFieldSchema(fieldName, schema); - // check for existence of nested schema - const subSchema = getNestedSchema(fieldName, schema); - // if nested schema exists, call convertSchema recursively - if (subSchema) { - const convertedSubSchema = convertSchema(subSchema); - if (flatten) { - jsonSchema = { ...jsonSchema, ...convertedSubSchema }; + // check for existence of nested field + // and get its schema if possible or its type otherwise + const subSchemaOrType = getNestedFieldSchemaOrType(fieldName, schema); + if (subSchemaOrType) { + // if nested field exists, call convertSchema recursively + const convertedSubSchema = convertSchema(subSchemaOrType); + // nested schema can be a field schema ({type, canRead, etc.}) (convertedSchema will be null) + // or a schema on its own with subfields (convertedSchema will return smth) + if (!convertedSubSchema) { + // subSchema is a simple field in this case (eg array of numbers) + jsonSchema[fieldName].field = getFieldSchema(`${fieldName}.$`, schema); } else { - jsonSchema[fieldName].schema = convertedSubSchema; + // subSchema is a full schema with multiple fields (eg array of objects) + if (flatten) { + jsonSchema = { ...jsonSchema, ...convertedSubSchema }; + } else { + jsonSchema[fieldName].schema = convertedSubSchema; + } } } }); @@ -51,43 +116,43 @@ export const getFieldSchema = (fieldName, schema) => { return fieldSchema; }; - // type is an array due to the possibility of using SimpleSchema.oneOf // right now we support only fields with one type -export const getSchemaType = schema => schema.type.definitions[0].type +export const getSchemaType = schema => schema.type.definitions[0].type; + const getArrayNestedSchema = (fieldName, schema) => { const arrayItemSchema = schema._schema[`${fieldName}.$`]; - const nestedSchema = arrayItemSchema && getSchemaType(arrayItemSchema) - return nestedSchema -} + const nestedSchema = arrayItemSchema && getSchemaType(arrayItemSchema); + return nestedSchema; +}; // nested object fields type is of the form "type: new SimpleSchema({...})" // so they should possess a "_schema" prop -const isNestedSchemaField = (fieldSchema) => { - const fieldType = getSchemaType(fieldSchema) +const isNestedSchemaField = fieldSchema => { + const fieldType = getSchemaType(fieldSchema); //console.log('fieldType', typeof fieldType, fieldType._schema) - return fieldType && !!fieldType._schema -} + return fieldType && !!fieldType._schema; +}; const getObjectNestedSchema = (fieldName, schema) => { - const fieldSchema = schema._schema[fieldName] - if (!isNestedSchemaField(fieldSchema)) return null - const nestedSchema = fieldSchema && getSchemaType(fieldSchema) - return nestedSchema -} + const fieldSchema = schema._schema[fieldName]; + if (!isNestedSchemaField(fieldSchema)) return null; + const nestedSchema = fieldSchema && getSchemaType(fieldSchema); + return nestedSchema; +}; /* Given an array field, get its nested schema - +If the field is not an object, this will return the subfield type instead */ -export const getNestedSchema = (fieldName, schema) => { - const arrayItemSchema = getArrayNestedSchema(fieldName, schema) +export const getNestedFieldSchemaOrType = (fieldName, schema) => { + const arrayItemSchema = getArrayNestedSchema(fieldName, schema); if (!arrayItemSchema) { // look for an object schema - const objectItemSchema = getObjectNestedSchema(fieldName, schema) + const objectItemSchema = getObjectNestedSchema(fieldName, schema); // no schema was found - if (!objectItemSchema) return null - return objectItemSchema + if (!objectItemSchema) return null; + return objectItemSchema; } - return arrayItemSchema + return arrayItemSchema; }; export const schemaProperties = [ @@ -136,7 +201,7 @@ export const schemaProperties = [ 'options', 'query', 'fieldProperties', - 'intl', + 'intl' ]; export const formProperties = [ @@ -168,5 +233,5 @@ export const formProperties = [ 'placeholder', 'options', 'query', - 'fieldProperties', + 'fieldProperties' ]; diff --git a/packages/vulcan-forms/lib/modules/utils.js b/packages/vulcan-forms/lib/modules/utils.js index b394dee96..05638315d 100644 --- a/packages/vulcan-forms/lib/modules/utils.js +++ b/packages/vulcan-forms/lib/modules/utils.js @@ -1,4 +1,3 @@ -import Users from 'meteor/vulcan:users'; import merge from 'lodash/merge'; import find from 'lodash/find'; import isPlainObject from 'lodash/isPlainObject'; @@ -8,10 +7,10 @@ import size from 'lodash/size'; import { removePrefix, filterPathsByPrefix } from './path_utils'; // add support for nested properties -export const deepValue = function(obj, path){ +export const deepValue = function(obj, path) { const pathArray = path.split('.'); - for (var i=0; i < pathArray.length; i++) { + for (var i = 0; i < pathArray.length; i++) { obj = obj[pathArray[i]]; } @@ -21,56 +20,27 @@ export const deepValue = function(obj, path){ // see http://stackoverflow.com/questions/19098797/fastest-way-to-flatten-un-flatten-nested-json-objects export const flatten = function(data) { var result = {}; - function recurse (cur, prop) { - + function recurse(cur, prop) { if (Object.prototype.toString.call(cur) !== '[object Object]') { result[prop] = cur; } else if (Array.isArray(cur)) { - for(var i=0, l=cur.length; i (typeof value === 'undefined' || value === null || value === '' || Array.isArray(value) && value.length === 0); +export const isEmptyValue = value => + typeof value === 'undefined' || value === null || value === '' || (Array.isArray(value) && value.length === 0); /** * Merges values. It takes into account the current, original and deleted values, @@ -85,14 +55,7 @@ export const isEmptyValue = value => (typeof value === 'undefined' || value === * @return {*|undefined} * Merged value or undefined if no merge was performed */ -export const mergeValue = ({ - currentValue, - documentValue, - deletedValues: deletedFields, - path, - locale, - datatype, -}) => { +export const mergeValue = ({ currentValue, documentValue, deletedValues: deletedFields, path, locale, datatype }) => { if (locale) { // note: intl fields are of type Object but should be treated as Strings return currentValue || documentValue || ''; @@ -132,10 +95,7 @@ export const mergeValue = ({ * // => { 'field': { 'subField': null, 'subFieldArray': [null] }, 'fieldArray': [null, undefined, { name: null } } */ export const getDeletedValues = (deletedFields, accumulator = {}) => - deletedFields.reduce( - (deletedValues, path) => set(deletedValues, path, null), - accumulator, - ); + deletedFields.reduce((deletedValues, path) => set(deletedValues, path, null), accumulator); /** * Filters the given field names by prefix, removes it from each one of them @@ -164,16 +124,13 @@ export const getDeletedValues = (deletedFields, accumulator = {}) => * // => [null, undefined, { 'name': null } ] */ export const getNestedDeletedValues = (prefix, deletedFields, accumulator = {}) => - getDeletedValues( - removePrefix(prefix, filterPathsByPrefix(prefix, deletedFields)), - accumulator, - ); + getDeletedValues(removePrefix(prefix, filterPathsByPrefix(prefix, deletedFields)), accumulator); export const getFieldType = datatype => datatype[0].type; /** * Get appropriate null value for various field types - * - * @param {Array} datatype + * + * @param {Array} datatype * Field's datatype property */ export const getNullValue = datatype => { @@ -190,4 +147,4 @@ export const getNullValue = datatype => { // normalize to null return null; } -} \ No newline at end of file +}; diff --git a/packages/vulcan-forms/package.js b/packages/vulcan-forms/package.js index 95a0852a1..9fd08e45c 100644 --- a/packages/vulcan-forms/package.js +++ b/packages/vulcan-forms/package.js @@ -1,20 +1,20 @@ Package.describe({ name: 'vulcan:forms', summary: 'Form containers for React', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/meteor-utilities/react-form-containers.git' }); Package.onUse(function (api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:core@1.12.8']); + api.use(['vulcan:core@1.12.13']); api.mainModule('lib/client/main.js', ['client']); api.mainModule('lib/server/main.js', ['server']); }); Package.onTest(function (api) { - api.use(['ecmascript', 'meteortesting:mocha', 'vulcan:forms']); + api.use(['ecmascript', 'meteortesting:mocha', 'vulcan:test', 'vulcan:forms']); api.mainModule('./test/index.js'); }); diff --git a/packages/vulcan-forms/test/components.test.js b/packages/vulcan-forms/test/components.test.js index b9aad4524..b65f6a09b 100644 --- a/packages/vulcan-forms/test/components.test.js +++ b/packages/vulcan-forms/test/components.test.js @@ -1,282 +1,462 @@ // setup JSDOM server side for testing (necessary for Enzyme to mount) -import 'jsdom-global/register' -import React from 'react' +import 'jsdom-global/register'; +import React from 'react'; // TODO: should be loaded from Components instead? -import Form from '../lib/components/Form' -import FormComponent from '../lib/components/FormComponent' -import '../lib/components/FormNestedArray' -import expect from 'expect' -import Enzyme, { mount, shallow } from 'enzyme' -import Adapter from 'enzyme-adapter-react-16'; -import { Components } from 'meteor/vulcan:core' +import Form from '../lib/components/Form'; +import FormComponent from '../lib/components/FormComponent'; +import '../lib/components/FormNestedArray'; +import expect from 'expect'; -// setup enzyme -// TODO: write a reusable helper and move this to the tests setup -Enzyme.configure({ adapter: new Adapter() }) +import { mount, shallow } from 'enzyme'; +import { Components } from 'meteor/vulcan:core'; +import { initComponentTest } from 'meteor/vulcan:test'; // we must import all the other components, so that "registerComponent" is called -import '../lib/modules/components' -// and then load them in the app so that is defined -import { populateComponentsApp, initializeFragments } from 'meteor/vulcan:lib' -// we need registered fragments to be initialized because populateComponentsApp will run -// hocs, like withUpdate, that rely on fragments -initializeFragments() -// actually fills the Components object -populateComponentsApp() - +import '../lib/modules/components'; +// setup Vulcan (load components, initialize fragments) +initComponentTest(); // fixtures import SimpleSchema from 'simpl-schema'; const addressGroup = { - name: 'addresses', - label: 'Addresses', - order: 10 + name: 'addresses', + label: 'Addresses', + order: 10 }; +const permissions = { + canRead: ['guests'], + canUpdate: ['quests'], + canCreate: ['guests'] +}; + +// just 1 input for state testing +const fooSchema = { + foo: { + type: String, + ...permissions + } +}; +// const addressSchema = { - street: { - type: String, - optional: true, - viewableBy: ['guests'], - editableBy: ['quests'], - insertableBy: ['quests'], - max: 100 // limit street address to 100 characters - }, + street: { + type: String, + optional: true, + ...permissions + } }; -const arraySchema = { - addresses: { - type: Array, - viewableBy: ['guests'], - editableBy: ['quests'], - insertableBy: ['quests'], - group: addressGroup - }, - 'addresses.$': { - type: new SimpleSchema(addressSchema) - } +// [{street, city,...}, {street, city, ...}] +const arrayOfObjectSchema = { + addresses: { + type: Array, + group: addressGroup, + ...permissions + }, + 'addresses.$': { + type: new SimpleSchema(addressSchema) + } }; +// example with custom inputs for the children +// ["http://maps/XYZ", "http://maps/foobar"] +const arrayOfUrlSchema = { + addresses: { + type: Array, + group: addressGroup, + ...permissions + }, + 'addresses.$': { + type: String, + input: 'url' + } +}; +// example with array and custom input +const CustomObjectInput = () => 'OBJECT INPUT'; +const arrayOfCustomObjectSchema = { + addresses: { + type: Array, + group: addressGroup, + ...permissions + }, + 'addresses.$': { + type: new SimpleSchema(addressSchema), + input: CustomObjectInput + } +}; +// example with a fully custom input for both the array and its children +const ArrayInput = () => 'ARRAY INPUT'; +const arrayFullCustomSchema = { + addresses: { + type: Array, + group: addressGroup, + ...permissions, + input: ArrayInput + }, + 'addresses.$': { + type: String, + input: 'url' + } +}; +// example with a native type +// ["20 rue du Moulin PARIS", "16 rue de la poste PARIS"] + +// eslint-disable-next-line no-unused-vars +const arrayOfStringSchema = { + addresses: { + type: Array, + group: addressGroup, + ...permissions + }, + 'addresses.$': { + type: String + } +}; +// object (not in an array): {street, city} const objectSchema = { - addresses: { - type: new SimpleSchema(addressSchema), - viewableBy: ['guests'], - editableBy: ['quests'], - insertableBy: ['quests'], - }, + addresses: { + type: new SimpleSchema(addressSchema), + ...permissions + } +}; +// without calling SimpleSchema +// eslint-disable-next-line no-unused-vars +const bareObjectSchema = { + addresses: { + type: addressSchema, + ...permissions + } }; // stub collection -import { createCollection, getDefaultResolvers, getDefaultMutations } from 'meteor/vulcan:core' -const WithArrays = createCollection({ - collectionName: 'WithArrays', - typeName: 'WithArray', - schema: arraySchema, - resolvers: getDefaultResolvers('WithArrays'), - mutations: getDefaultMutations('WithArrays'), -}); -const WithObjects = createCollection({ - collectionName: 'WithObjects', - typeName: 'WithObject', - schema: objectSchema, - resolvers: getDefaultResolvers('WithObjects'), - mutations: getDefaultMutations('WithObjects'), -}); +import { createCollection, getDefaultResolvers, getDefaultMutations } from 'meteor/vulcan:core'; +const createDummyCollection = (typeName, schema) => + createCollection({ + collectionName: typeName + 's', + typeName, + schema, + resolvers: getDefaultResolvers(typeName + 's'), + mutations: getDefaultMutations(typeName + 's') + }); +const Foos = createDummyCollection('Foo', fooSchema); +const ArrayOfObjects = createDummyCollection('ArrayOfObject', arrayOfObjectSchema); +const Objects = createDummyCollection('Object', objectSchema); +const ArrayOfUrls = createDummyCollection('ArrayOfUrl', arrayOfUrlSchema); +const ArrayOfCustomObjects = createDummyCollection('ArrayOfCustomObject', arrayOfCustomObjectSchema); +const ArrayFullCustom = createDummyCollection('ArrayFullCustom', arrayFullCustomSchema); +// eslint-disable-next-line no-unused-vars +const ArrayOfStrings = createDummyCollection('ArrayOfString', arrayOfStringSchema); const Addresses = createCollection({ - collectionName: 'Addresses', - typeName: 'Address', - schema: addressSchema, - resolvers: getDefaultResolvers('Addresses'), - mutations: getDefaultMutations('Addresses'), -}) + collectionName: 'Addresses', + typeName: 'Address', + schema: addressSchema, + resolvers: getDefaultResolvers('Addresses'), + mutations: getDefaultMutations('Addresses') +}); +// helpers // tests -describe('vulcan-forms/components', function () { - describe('Form', function () { - const context = { - intl: { - formatMessage: () => '', - formatDate: () => '', - formatTime: () => '', - formatRelative: () => '', - formatNumber: () => '', - formatPlural: () => '', - formatHTMLMessage: () => '' - } +describe('vulcan-forms/components', function() { + const context = { + intl: { + formatMessage: () => '', + formatDate: () => '', + formatTime: () => '', + formatRelative: () => '', + formatNumber: () => '', + formatPlural: () => '', + formatHTMLMessage: () => '', + now: () => '' + } + }; + // eslint-disable-next-line no-unused-vars + const mountWithContext = C => + mount(C, { + context + }); + const shallowWithContext = C => + shallow(C, { + context + }); + describe('Form collectionName="" (handle fields computation)', function() { + // since some props are now handled by HOC we need to provide them manually + const defaultProps = { + collectionName: '', + typeName: '' + }; + + describe('Form generation', function() { + // getters + const getArrayFormGroup = wrapper => wrapper.find('FormGroup').find({ name: 'addresses' }); + const getFields = arrayFormGroup => arrayFormGroup.prop('fields'); + describe('basic collection - no nesting', function() { + it('shallow render', function() { + const wrapper = shallowWithContext(); + expect(wrapper).toBeDefined(); + }); + }); + describe('nested object (not in array)', function() { + it('shallow render', () => { + const wrapper = shallowWithContext(); + expect(wrapper).toBeDefined(); + }); + it('define one field', () => { + const wrapper = shallowWithContext(); + const defaultGroup = wrapper.find('FormGroup').first(); + const fields = defaultGroup.prop('fields'); + expect(fields).toHaveLength(1); // addresses field + }); + + const getFormFields = wrapper => { + const defaultGroup = wrapper.find('FormGroup').first(); + const fields = defaultGroup.prop('fields'); + return fields; + }; + const getFirstField = () => { + const wrapper = shallowWithContext(); + const fields = getFormFields(wrapper); + return fields[0]; + }; + it('define the nestedSchema', () => { + const addressField = getFirstField(); + expect(addressField.nestedSchema.street).toBeDefined(); + }); + }); + describe('array of objects', function() { + it('shallow render', () => { + const wrapper = shallowWithContext(); + expect(wrapper).toBeDefined(); + }); + it('render a FormGroup for addresses', function() { + const wrapper = shallowWithContext(); + const formGroup = wrapper.find('FormGroup').find({ name: 'addresses' }); + expect(formGroup).toBeDefined(); + expect(formGroup).toHaveLength(1); + }); + it('passes down the array child fields', function() { + const wrapper = shallowWithContext(); + const formGroup = getArrayFormGroup(wrapper); + const fields = getFields(formGroup); + const arrayField = fields[0]; + expect(arrayField.nestedInput).toBe(true); + expect(arrayField.nestedFields).toHaveLength(Object.keys(addressSchema).length); + }); + }); + describe('array with custom children inputs (e.g array of url)', function() { + it('shallow render', function() { + const wrapper = shallowWithContext(); + expect(wrapper).toBeDefined(); + }); + it('passes down the array item custom input', () => { + const wrapper = shallowWithContext(); + const formGroup = getArrayFormGroup(wrapper); + const fields = getFields(formGroup); + const arrayField = fields[0]; + expect(arrayField.arrayField).toBeDefined(); + }); + }); + describe('array of objects with custom children inputs', function() { + it('shallow render', function() { + const wrapper = shallowWithContext(); + expect(wrapper).toBeDefined(); + }); + // TODO: does not work, schema_utils needs an update + it.skip('passes down the custom input', function() { + const wrapper = shallowWithContext(); + const formGroup = getArrayFormGroup(wrapper); + const fields = getFields(formGroup); + const arrayField = fields[0]; + expect(arrayField.arrayField).toBeDefined(); + }); + }); + describe('array with a fully custom input (array itself and children)', function() { + it('shallow render', function() { + const wrapper = shallowWithContext(); + expect(wrapper).toBeDefined(); + }); + it('passes down the custom input', function() { + const wrapper = shallowWithContext(); + const formGroup = getArrayFormGroup(wrapper); + const fields = getFields(formGroup); + const arrayField = fields[0]; + expect(arrayField.arrayField).toBeDefined(); + }); + }); + }); + + describe('Form state management', function() { + // TODO: the change callback is triggerd but `foo` becomes null instead of "bar + // so it's added to the deletedValues and not changedValues + it.skip('store typed value', function() { + const wrapper = mountWithContext(); + //console.log(wrapper.state()); + wrapper + .find('input') + .first() + .simulate('change', { target:{value:'bar'} }); + // eslint-disable-next-line no-console + console.log(wrapper.find('input').first().html()); + // eslint-disable-next-line no-console + console.log(wrapper.state()); + expect(wrapper.state().currentValues).toEqual({foo:'bar'}); + }); + it('reset state when relevant props change', function() { + const wrapper = shallowWithContext(); + wrapper.setState({ currentValues: { foo: 'bar' } }); + expect(wrapper.state('currentValues')).toEqual({foo:'bar'}); + wrapper.setProps({ collectionName: 'Bars' }); + expect(wrapper.state('currentValues')).toEqual({}); + }); + it('does not reset state when external prop change', function(){ + //const prefilledProps = { bar: 'foo' } // TODO + const changeCallback= () => 'CHANGE'; + const wrapper = shallowWithContext(); + wrapper.setState({ currentValues: { foo: 'bar' } }); + expect(wrapper.state('currentValues')).toEqual({foo:'bar'}); + const newChangeCallback = () => 'NEW'; + wrapper.setProps({ changeCallback: newChangeCallback }); + expect(wrapper.state('currentValues')).toEqual({ foo:'bar'}); + }); + }); + }); + + describe('FormComponent (select the components to render and handle state)', function() { + const shallowWithContext = C => + shallow(C, { + context: { + getDocument: () => {} } - // eslint-disable-next-line no-unused-vars - const mountWithContext = C => mount(C, { - context - }) - const shallowWithContext = C => shallow(C, { - context - }) - describe('basic', function () { - it('shallow render', function () { - const wrapper = shallowWithContext() - expect(wrapper).toBeDefined() - }) - }) - describe('nested array', function () { - it('shallow render', () => { - const wrapper = shallowWithContext() - expect(wrapper).toBeDefined() - }) - it('render a FormGroup for addresses', function () { - const wrapper = shallowWithContext() - const formGroup = wrapper.find('FormGroup').find({ name: 'addresses' }) - expect(formGroup).toBeDefined() - expect(formGroup).toHaveLength(1) - }) - }) - describe('nested object', function () { - it('shallow render', () => { - const wrapper = shallowWithContext() - expect(wrapper).toBeDefined() - }) - it('define one field', () => { - const wrapper = shallowWithContext() - const defaultGroup = wrapper.find('FormGroup').first() - const fields = defaultGroup.prop('fields') - expect(fields).toHaveLength(1) // addresses field - }) + }); + const defaultProps = { + disabled: false, + optional: true, + document: {}, + name: 'meetingPlace', + path: 'meetingPlace', + datatype: [{ type: Object }], + layout: 'horizontal', + label: 'Meeting place', + currentValues: {}, + formType: 'new', + deletedValues: [], + throwError: () => {}, + updateCurrentValues: () => {}, + errors: [], + clearFieldErrors: () => {} + }; + it('shallow render', function() { + const wrapper = shallowWithContext(); + expect(wrapper).toBeDefined(); + }); + describe('array of objects', function() { + const props = { + ...defaultProps, + datatype: [{ type: Array }], + nestedSchema: { + street: {}, + country: {}, + zipCode: {} + }, + nestedInput: true, + nestedFields: [{}, {}, {}], + currentValues: {} + }; + it('render a FormNestedArray', function() { + const wrapper = shallowWithContext(); + const formNested = wrapper.find('FormNestedArray'); + expect(formNested).toHaveLength(1); + }); + }); + describe('nested object', function() { + const props = { + ...defaultProps, + datatype: [{ type: new SimpleSchema({}) }], + nestedSchema: { + street: {}, + country: {}, + zipCode: {} + }, + nestedInput: true, + nestedFields: [{}, {}, {}], + currentValues: {} + }; + it('shallow render', function() { + const wrapper = shallowWithContext(); + expect(wrapper).toBeDefined(); + }); + it('render a FormNestedObject', function() { + const wrapper = shallowWithContext(); + const formNested = wrapper.find('FormNestedObject'); + expect(formNested).toHaveLength(1); + }); + }); + describe('array of custom inputs (e.g url)', function() { + it('shallow render', function() {}); + }); + }); - const getFormFields = (wrapper) => { - const defaultGroup = wrapper.find('FormGroup').first() - const fields = defaultGroup.prop('fields') - return fields - } - const getFirstField = () => { - const wrapper = shallowWithContext() - const fields = getFormFields(wrapper) - return fields[0] - } - it('define the nestedSchema', () => { - const addressField = getFirstField() - expect(addressField.nestedSchema.street).toBeDefined() - }) - }) - }) - - describe('FormComponent', function () { - const shallowWithContext = C => shallow(C, { - context: { - getDocument: () => { } - } - }) - const defaultProps = { - 'disabled': false, - 'optional': true, - 'document': {}, - 'name': 'meetingPlace', - 'path': 'meetingPlace', - 'datatype': [{ type: Object }], - 'layout': 'horizontal', - 'label': 'Meeting place', - 'currentValues': {}, - 'formType': 'new', - deletedValues: [], - throwError: () => { }, - updateCurrentValues: () => { }, - errors: [], - clearFieldErrors: () => { }, - } - it('shallow render', function () { - const wrapper = shallowWithContext() - expect(wrapper).toBeDefined() - }) - describe('nested array', function () { - const props = { - ...defaultProps, - 'datatype': [{ type: Array }], - 'nestedSchema': { - 'street': {}, - 'country': {}, - 'zipCode': {} - }, - 'nestedInput': true, - 'nestedFields': [ - {}, - {}, - {} - ], - 'currentValues': {}, - } - it('render a FormNestedArray', function () { - const wrapper = shallowWithContext() - const formNested = wrapper.find('FormNestedArray') - expect(formNested).toHaveLength(1) - }) - }) - describe('nested object', function () { - const props = { - ...defaultProps, - 'datatype': [{ type: new SimpleSchema({}) }], - 'nestedSchema': { - 'street': {}, - 'country': {}, - 'zipCode': {} - }, - 'nestedInput': true, - 'nestedFields': [ - {}, - {}, - {} - ], - 'currentValues': {}, - } - it('shallow render', function () { - const wrapper = shallowWithContext() - expect(wrapper).toBeDefined() - }) - it('render a FormNestedObject', function () { - const wrapper = shallowWithContext() - const formNested = wrapper.find('FormNestedObject') - expect(formNested).toHaveLength(1) - }) - }) - }) - - describe('FormNestedArray', function () { - it('shallow render', function () { - const wrapper = shallow() - expect(wrapper).toBeDefined() - }) - it('shows a button', function () { - const wrapper = shallow() - const button = wrapper.find('BootstrapButton') - expect(button).toHaveLength(1) - }) - it('shows an add button', function () { - const wrapper = shallow() - const addButton = wrapper.find('IconAdd') - expect(addButton).toHaveLength(1) - }) - }) - describe('FormNestedObject', function () { - it('shallow render', function () { - const wrapper = shallow() - expect(wrapper).toBeDefined() - }) - it.skip('render a form for the object', function () { - // eslint-disable-next-line no-unused-vars - const wrapper = shallow() - expect(false).toBe(true) - }) - it('does not show any button', function () { - const wrapper = shallow() - const button = wrapper.find('BootstrapButton') - expect(button).toHaveLength(0) - }) - it('does not show add button', function () { - const wrapper = shallow() - const addButton = wrapper.find('IconAdd') - expect(addButton).toHaveLength(0) - }) - it('does not show remove button', function () { - const wrapper = shallow() - const removeButton = wrapper.find('IconRemove') - expect(removeButton).toHaveLength(0) - }) - }) -}) \ No newline at end of file + describe('FormNestedArray - Display the input n times', function() { + const defaultProps = { + errors: [], + deletedValues: [], + path: 'foobar', + formComponents: Components + }; + it('shallow render', function() { + const wrapper = shallow(); + expect(wrapper).toBeDefined(); + }); + // TODO: broken now we use a layout... + it.skip('shows an add button when empty', function() { + const wrapper = mount(); + const addButton = wrapper.find('IconAdd'); + expect(addButton).toHaveLength(1); + }); + it.skip('shows 3 items', function() { + const wrapper = mount(); + const nestedItem = wrapper.find('FormNestedItem'); + expect(nestedItem).toHaveLength(3); + }); + it.skip('pass the correct path and itemIndex to each form', function() { + const wrapper = mount(); + const nestedItem = wrapper.find('FormNestedItem'); + const item0 = nestedItem.at(0); + const item1 = nestedItem.at(1); + expect(item0.prop('itemIndex')).toEqual(0); + expect(item1.prop('itemIndex')).toEqual(1); + expect(item0.prop('path')).toEqual('foobar.0'); + expect(item1.prop('path')).toEqual('foobar.1'); + }); + }); + describe('FormNestedObject', function() { + const defaultProps = { + errors: [], + path: 'foobar', + formComponents: Components + }; + it('shallow render', function() { + const wrapper = shallow(); + expect(wrapper).toBeDefined(); + }); + it.skip('render a Form collectionName="" for the object', function() { + // eslint-disable-next-line no-unused-vars + const wrapper = shallow(); + expect(false).toBe(true); + }); + it('does not show any button', function() { + const wrapper = shallow(); + const button = wrapper.find('BootstrapButton'); + expect(button).toHaveLength(0); + }); + it('does not show add button', function() { + const wrapper = shallow(); + const addButton = wrapper.find('IconAdd'); + expect(addButton).toHaveLength(0); + }); + it('does not show remove button', function() { + const wrapper = shallow(); + const removeButton = wrapper.find('IconRemove'); + expect(removeButton).toHaveLength(0); + }); + }); +}); diff --git a/packages/vulcan-forms/test/index.js b/packages/vulcan-forms/test/index.js index 6fc1b82ed..4e50bfdba 100644 --- a/packages/vulcan-forms/test/index.js +++ b/packages/vulcan-forms/test/index.js @@ -1,2 +1,2 @@ -import './schema_utils.test.js' -import './components.test.js' \ No newline at end of file +import './schema_utils.test.js'; +import './components.test.js'; diff --git a/packages/vulcan-forms/test/package.test.js b/packages/vulcan-forms/test/package.test.js index 3a732370d..d78210bfa 100644 --- a/packages/vulcan-forms/test/package.test.js +++ b/packages/vulcan-forms/test/package.test.js @@ -1,7 +1,7 @@ import FormWrapper from 'meteor/vulcan:forms'; -import expect from 'expect' +import expect from 'expect'; describe('vulcan:forms', function () { it.skip('initialize', function () { - expect(FormWrapper.name).toEqual('GraphQL') - }) -}) \ No newline at end of file + expect(FormWrapper.name).toEqual('GraphQL'); + }); +}); \ No newline at end of file diff --git a/packages/vulcan-forms/test/schema_utils.test.js b/packages/vulcan-forms/test/schema_utils.test.js index f6ccb531c..ff2f45cef 100644 --- a/packages/vulcan-forms/test/schema_utils.test.js +++ b/packages/vulcan-forms/test/schema_utils.test.js @@ -1,51 +1,97 @@ -import { getNestedSchema } from '../lib/modules/schema_utils.js' -import SimpleSchema from 'simpl-schema' -import expect from 'expect' +import { + getNestedFieldSchemaOrType, + getValidFields, + getCreateableFields, + getReadableFields, + getUpdateableFields +} from '../lib/modules/schema_utils.js'; +import SimpleSchema from 'simpl-schema'; +import expect from 'expect'; const addressSchema = { - street: { - type: String, - }, - country: { - type: String, - }, -} -const addressSimpleSchema = new SimpleSchema(addressSchema) + street: { + type: String + }, + country: { + type: String + } +}; +const addressSimpleSchema = new SimpleSchema(addressSchema); -describe('schema_utils', function () { - describe('getNestedSchema', function () { - it('get nested schema of an array', function () { - const simpleSchema = new SimpleSchema({ - addresses: { - type: Array - }, - 'addresses.$': { - // this is due to SimpleSchema objects structure - type: addressSimpleSchema - } - }) - const nestedSchema = getNestedSchema('addresses', simpleSchema) - // nestedSchema is a complex SimpleSchema object, so we can only - // test its type instead (might not be the simplest way though) - expect(Object.keys(nestedSchema._schema)).toEqual(Object.keys(addressSchema)) - }) - it('get nested schema of an object', function () { - const simpleSchema = new SimpleSchema({ - meetingPlace: { - type: addressSimpleSchema - } - }) - const nestedSchema = getNestedSchema('meetingPlace', simpleSchema) - expect(Object.keys(nestedSchema._schema)).toEqual(Object.keys(addressSchema)) - }) - it('return null for other types', function () { - const simpleSchema = new SimpleSchema({ - createdAt: { - type: Date - } - }) - const nestedSchema = getNestedSchema('createdAt', simpleSchema) - expect(nestedSchema).toBeNull() - }) - }) -}) \ No newline at end of file +describe('schema_utils', function() { + describe('getNestedFieldSchemaOrType', function() { + it('get nested schema of an array', function() { + const simpleSchema = new SimpleSchema({ + addresses: { + type: Array + }, + 'addresses.$': { + // this is due to SimpleSchema objects structure + type: addressSimpleSchema + } + }); + const nestedSchema = getNestedFieldSchemaOrType('addresses', simpleSchema); + // nestedSchema is a complex SimpleSchema object, so we can only + // test its type instead (might not be the simplest way though) + expect(Object.keys(nestedSchema._schema)).toEqual(Object.keys(addressSchema)); + }); + it('get nested schema of an object', function() { + const simpleSchema = new SimpleSchema({ + meetingPlace: { + type: addressSimpleSchema + } + }); + const nestedSchema = getNestedFieldSchemaOrType('meetingPlace', simpleSchema); + expect(Object.keys(nestedSchema._schema)).toEqual(Object.keys(addressSchema)); + }); + it('return null for other types', function() { + const simpleSchema = new SimpleSchema({ + createdAt: { + type: Date + } + }); + const nestedSchema = getNestedFieldSchemaOrType('createdAt', simpleSchema); + expect(nestedSchema).toBeNull(); + }); + }); + describe('fields extraction', function() { + describe('valid', function() { + it('remove invalid fields', function() { + const schema = { + validField: {}, + arrayField: {}, + // array child + 'arrayField.$': {} + }; + expect(getValidFields(schema)).toEqual(['validField', 'arrayField']); + }); + }); + describe('readable', function() { + it('get readable field', function() { + const schema = { + readable: { canRead: [] }, + notReadble: {} + }; + expect(getReadableFields(schema)).toEqual(['readable']); + }); + }); + describe('creatable', function() { + it('get creatable field', function() { + const schema = { + creatable: { canCreate: [] }, + notCreatable: {} + }; + expect(getCreateableFields(schema)).toEqual(['creatable']); + }); + }); + describe('updatable', function() { + it('get updatable field', function() { + const schema = { + updatable: { canUpdate: [] }, + notUpdatable: {} + }; + expect(getUpdateableFields(schema)).toEqual(['updatable']); + }); + }); + }); +}); diff --git a/packages/vulcan-i18n-en-us/lib/en_US.js b/packages/vulcan-i18n-en-us/lib/en_US.js index bcedfdb8f..a3d51b219 100644 --- a/packages/vulcan-i18n-en-us/lib/en_US.js +++ b/packages/vulcan-i18n-en-us/lib/en_US.js @@ -131,11 +131,28 @@ addStrings('en', { 'cards.edit': 'Edit', 'datatable.new': 'New', 'datatable.edit': 'Edit', + 'datatable.search': 'Search', 'admin': 'Admin', 'notifications': 'Notifications', - 'errors.expectedType': 'Expected a field “{label}” of type {dataType}, got “{value}” instead.', + 'errors.expectedType': 'Expected type {dataType} for field “{label}”, received “{value}” instead.', 'errors.required': 'Field “{label}” is required.', + 'errors.minString': 'Field “{label}” needs to have at least {min} characters', 'errors.maxString': 'Field “{label}” is limited to {max} characters.', -}); + 'errors.generic': 'Sorry, something went wrong: {errorMessage}.', + 'errors.generic_report': 'Sorry, something went wrong: {errorMessage}.
An error report has been generated.', + 'errors.minNumber': 'Field “{label}” must be higher than {min}. ', + 'errors.maxNumber': 'Field “{label}” must be lower than {max}. ', + 'errors.minCount': 'There needs to be at least {count} in field “{label}”.', + 'errors.maxCount': 'Field “{label}” is only allowed {count}.', + 'errors.regEx': 'Field “{label}”: wrong formatting', + 'errors.badDate': 'Field “{label}” is not a date.', + 'errors.notAllowed': 'The value for field “{label}” is not allowed.', + 'errors.noDecimal': 'The value for field “{label}” must not be a decimal number.', + //TODO other simple schema errors + 'errors.minNumberExclusive': '', + 'errors.maxNumberExclusive': '', + 'errors.keyNotInSchema': '' + +}); \ No newline at end of file diff --git a/packages/vulcan-i18n-en-us/package.js b/packages/vulcan-i18n-en-us/package.js index ce4af3c30..66a417c6a 100644 --- a/packages/vulcan-i18n-en-us/package.js +++ b/packages/vulcan-i18n-en-us/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:i18n-en-us', summary: 'Vulcan i18n package (en_US)', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -10,7 +10,7 @@ Package.onUse(function (api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:core@1.12.8' + 'vulcan:core@1.12.13' ]); api.addFiles([ diff --git a/packages/vulcan-i18n-es-es/lib/es_ES.js b/packages/vulcan-i18n-es-es/lib/es_ES.js index 03b9353d4..2d9b4facc 100644 --- a/packages/vulcan-i18n-es-es/lib/es_ES.js +++ b/packages/vulcan-i18n-es-es/lib/es_ES.js @@ -127,9 +127,6 @@ addStrings('es', { 'app.disallowed_property_detected': 'Propiedad no permitida detectada: {value}', 'app.something_bad_happened': 'Algo malo pasó...', 'app.embedly_not_authorized': 'Clave API incrustada no válida incluida en el archivo de configuración. Para encontrar su clave, inicie sesión en https://app.embed.ly -> API', - 'app.required_field_missing': '{fieldName} es obligatorio.', - 'app.field_is_too_long': '{fieldName} no puede superar {limit} caracteres.', - 'app.schema_validation_error': 'Error de validación de esquema', 'cards.edit': 'Editar', 'datatable.new': 'Nuevo', @@ -140,5 +137,20 @@ addStrings('es', { 'errors.expectedType': 'Se esperaba un campo “{label}” de tipo {dataType}, se ha recibido “{value}” en su lugar.', 'errors.required': 'El campo “{label}” es obligatorio.', - 'errors.maxString': 'El campo “{label}” está limitado a {max} carácteres.', + 'errors.minString': 'El campo “{label}” debe tener al menos {max} caracteres.', + 'errors.maxString': 'El campo “{label}” está limitado a {max} caracteres.', + 'errors.generic':'Ha ocurrido un error: {errorMessage}', + 'errors.generic_report':'Algo ha ido mal: {errorMessage}.
Se ha reportado el error.', + 'errors.minNumber':'El campo “{label}” debe ser superior a {min}.', + 'errors.maxNumber':'El campo “{label}” debe ser inferior a {max}.', + 'errors.minCount':'El campo “{label}” debe tener al menos {count}.', + 'errors.maxCount':'El campo “{label}” está limitado a {count}.', + 'errors.regEx':'El campo “{label}” está mal formateado.', + 'errors.badDate':'El campo “{label}” debe ser una fecha.', + 'errors.notAllowed':'El valor del campo “{label}” no està permitido.', + 'errors.noDecimal':'El campo “{label}” no puede ser un decimal.', + + 'errors.minNumberExclusive':'', + 'errors.maxNumberExclusive':'', + 'errors.keyNotInSchema':'', }); diff --git a/packages/vulcan-i18n-es-es/package.js b/packages/vulcan-i18n-es-es/package.js index 65826903f..47abe4fd5 100644 --- a/packages/vulcan-i18n-es-es/package.js +++ b/packages/vulcan-i18n-es-es/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:i18n-es-es', summary: 'Vulcan i18n package (es_ES)', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -10,7 +10,7 @@ Package.onUse(function (api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:core@1.12.8' + 'vulcan:core@1.12.13' ]); api.addFiles([ diff --git a/packages/vulcan-i18n-fr-fr/lib/fr_FR.js b/packages/vulcan-i18n-fr-fr/lib/fr_FR.js index 040a9886d..d1d895ea1 100644 --- a/packages/vulcan-i18n-fr-fr/lib/fr_FR.js +++ b/packages/vulcan-i18n-fr-fr/lib/fr_FR.js @@ -33,7 +33,7 @@ addStrings('fr', { 'accounts.sign_in': 'Connexion', 'accounts.sign_out': 'Se déconnecter', 'accounts.cancel': 'Annuler', - 'accounts.or_use': 'ou utiliser', + 'accounts.or_use': 'ou utilisez', 'accounts.info_email_sent': 'Email envoyé.', 'accounts.info_password_changed': 'Mot de passe changé.', 'accounts.logging_in': 'Connexion en cours…', @@ -129,18 +129,31 @@ addStrings('fr', { 'app.disallowed_property_detected': 'Propriété refusée détectée: {value}', 'app.something_bad_happened': 'Quelque chose s\'est mal passé...', 'app.embedly_not_authorized': 'Clé d\'API Embedly invalide renseignée dans les paramètres. Pour trouver votre clé, connectez-vous sur: https://app.embed.ly -> API', - 'app.required_field_missing': '{fieldName} requis.', - 'app.field_is_too_long': '{fieldName} ne peut pas dépasser {limit} caractères.', - 'app.schema_validation_error': 'Erreur de validation du schéma', 'cards.edit': 'Modifier', 'datatable.new': 'Nouveau', 'datatable.edit': 'Modifier', - + 'datatable.search': 'Rechercher', + 'admin': 'Admin', 'notifications': 'Notifications', 'errors.expectedType': 'Un champ “{label}” de type {dataType} était attendu, “{value}” a été reçu à la place.', 'errors.required': 'Le champ “{label}” est requis.', + 'errors.minString': 'Le champ "{label}" doit faire au moins {min} caractères.', 'errors.maxString': 'Le champ “{label}” est limité à {max} caractères.', + 'errors.generic':'Désolé, une erreur est survenue: {errorMessage}', + 'errors.generic_report':'Désolé, une erreur est survenue: {errorMessage}.
Un message d\'erreur a été envoyé.', + 'errors.minNumber':'Le champ “{label}” doit être supérieur à {min}.', + 'errors.maxNumber':'Le champ “{label}” doit être inférieur à {max}.', + 'errors.minCount':'Il faut au moins {count} objets dans le champ “{label}”.', + 'errors.maxCount':'Le champ “{label}” est limité à {count} objets', + 'errors.regEx':'Le champ “{label}” est mal formatté', + 'errors.badDate':'Le champ “{label}” n\'est pas une date', + 'errors.notAllowed':'La valeur du champ "{label}" est interdite.', + 'errors.noDecimal':'La valeur du champ "{label}" ne peut être décimale.', + + 'errors.minNumberExclusive':'', + 'errors.maxNumberExclusive':'', + 'errors.keyNotInSchema':'', }); diff --git a/packages/vulcan-i18n-fr-fr/package.js b/packages/vulcan-i18n-fr-fr/package.js index 67c4444ad..607aa80d8 100644 --- a/packages/vulcan-i18n-fr-fr/package.js +++ b/packages/vulcan-i18n-fr-fr/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:i18n-fr-fr', summary: 'Vulcan i18n package (fr_FR)', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -10,7 +10,7 @@ Package.onUse(function (api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:core@1.12.8' + 'vulcan:core@1.12.13' ]); api.addFiles([ diff --git a/packages/vulcan-i18n/lib/modules/message.js b/packages/vulcan-i18n/lib/modules/message.js index 963808e9e..fb47d2b26 100644 --- a/packages/vulcan-i18n/lib/modules/message.js +++ b/packages/vulcan-i18n/lib/modules/message.js @@ -7,11 +7,11 @@ const FormattedMessage = ({ id, values, defaultMessage = '', html = false, class return html ? : - {message} -} + {message}; +}; FormattedMessage.contextTypes = { intl: intlShape -} +}; export default FormattedMessage; diff --git a/packages/vulcan-i18n/lib/modules/provider.js b/packages/vulcan-i18n/lib/modules/provider.js index d28975306..ed132aad0 100644 --- a/packages/vulcan-i18n/lib/modules/provider.js +++ b/packages/vulcan-i18n/lib/modules/provider.js @@ -1,19 +1,11 @@ import React, { Component } from 'react'; -import { Strings } from 'meteor/vulcan:lib'; +import { getString } from 'meteor/vulcan:lib'; import { intlShape } from './shape.js'; export default class IntlProvider extends Component{ formatMessage = ({ id, defaultMessage }, values) => { - const messages = Strings[this.props.locale] || {}; - let message = messages[id] || defaultMessage; - if (values) { - _.forEach(values, (value, key) => { - // note: see replaceAll definition in vulcan:lib/utils - message = message.replaceAll(`{${key}}`, value); - }); - } - return message; + return getString({ id, defaultMessage, values, locale: this.props.locale }); } formatStuff = (something) => { @@ -43,4 +35,4 @@ export default class IntlProvider extends Component{ IntlProvider.childContextTypes = { intl: intlShape -} +}; diff --git a/packages/vulcan-i18n/package.js b/packages/vulcan-i18n/package.js index acf44b304..3fa82e589 100644 --- a/packages/vulcan-i18n/package.js +++ b/packages/vulcan-i18n/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:i18n', summary: 'i18n client polyfill', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan' }); @@ -10,7 +10,7 @@ Package.onUse(function (api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:lib@1.12.8', + 'vulcan:lib@1.12.13', ]); api.mainModule('lib/server/main.js', 'server'); diff --git a/packages/vulcan-lib/lib/client/render_context.js b/packages/vulcan-lib/lib/client/render_context.js deleted file mode 100644 index a812c6e4f..000000000 --- a/packages/vulcan-lib/lib/client/render_context.js +++ /dev/null @@ -1,76 +0,0 @@ -// import { browserHistory } from 'react-router'; -// import { compose } from 'redux'; - -// import { -// createApolloClient, -// configureStore, -// addAction, getActions, addReducer, getReducers, addMiddleware, getMiddlewares, -// } from '../modules/index.js'; - -// let context; - -// export const initContext = () => { - -// // init -// const history = browserHistory; -// const loginToken = global.localStorage['Meteor.loginToken']; -// let apolloClient; - -// // init context -// context = { -// history, -// loginToken, -// addAction, // context.addAction same as addAction -// getActions, // context.getActions same as getActions -// addReducer, // context.addReducer same as addReducer -// getReducers, // context.getReducers same as getReducers -// addMiddleware, // context.addMiddleware same as addMiddleware -// getMiddlewares, // context.getMiddlewares same as getMiddlewares -// }; - -// // defer creation of apolloClient until it is first used -// Object.defineProperty(context, 'apolloClient', { -// enumerable: true, -// get: () => { -// if (!apolloClient) { -// apolloClient = createApolloClient(); -// addReducer({ apollo: apolloClient.reducer() }); -// addMiddleware(apolloClient.middleware()); -// } -// return apolloClient; -// }, -// }); - -// // init store -// context.store = configureStore(context.getReducers, {}, (store) => { -// let chain, newDispatch; -// return next => (action) => { -// if (!chain) { -// chain = context.getMiddlewares().map(middleware => middleware(store)); -// newDispatch = compose(...chain)(next) -// } -// return newDispatch(action); -// }; -// }) -// } - -// // render context object -// export const renderContext = { -// get: () => { - -// if (typeof context === 'undefined') { -// initContext(); -// } - -// return context - -// } -// }; - -// // render context get function -// export const getRenderContext = () => renderContext.get(); - -// // withRenderContext make it easy to access context -// export const withRenderContext = (func) => { -// func(context); -// }; diff --git a/packages/vulcan-lib/lib/modules/admin.js b/packages/vulcan-lib/lib/modules/admin.js index 071597fc4..ce11f8af7 100644 --- a/packages/vulcan-lib/lib/modules/admin.js +++ b/packages/vulcan-lib/lib/modules/admin.js @@ -6,4 +6,4 @@ export const addAdminColumn = columnOrColumns => { } else { AdminColumns.push(columnOrColumns); } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/packages/vulcan-lib/lib/modules/apollo.js b/packages/vulcan-lib/lib/modules/apollo.js index a9c3e9213..ff680b34f 100644 --- a/packages/vulcan-lib/lib/modules/apollo.js +++ b/packages/vulcan-lib/lib/modules/apollo.js @@ -9,7 +9,7 @@ registerSetting('graphQLendpointURL', '/graphql', 'GraphQL endpoint URL'); const defaultNetworkInterfaceConfig = { path: '/graphql', // default graphql server endpoint - opts: {}, // additional fetch options like `credentials` or `headers` + opts: { foo: 'bar' }, // additional fetch options like `credentials` or `headers`; note: for some reason when this is empty additional options can't be added useMeteorAccounts: true, // if true, send an eventual Meteor login token to identify the current user with every request batchingInterface: true, // use a BatchingNetworkInterface by default instead of a NetworkInterface batchInterval: 10, // default batch interval @@ -18,6 +18,10 @@ const defaultNetworkInterfaceConfig = { const createMeteorNetworkInterface = (givenConfig = {}) => { const config = { ...defaultNetworkInterfaceConfig, ...givenConfig }; + // console.log('// apollo.js createMeteorNetworkInterface config.headers'); + // console.log(config.headers); // note: only defined on server + // console.log('\n\n'); + // absoluteUrl adds a '/', so let's remove it first let path = config.path; if (path[0] === '/') { @@ -50,26 +54,41 @@ const createMeteorNetworkInterface = (givenConfig = {}) => { const networkInterface = interfaceToUse(interfaceOptions); - // console.log('// apollo.js locale:', config.locale); - if (config.useMeteorAccounts) { networkInterface.use([{ applyBatchMiddleware(request, next) { const currentUserToken = Meteor.isClient ? global.localStorage['Meteor.loginToken'] : config.loginToken; - if (!currentUserToken) { + if (Meteor.isServer) { + // handle server use case separetly or else everything breaks + if (!request.options.headers) { + request.options.headers = new Headers(); + } + request.options.headers = config.headers; + // if we're on the server and this request has been originated by a client + // (and not SSR) save the original headers + if (config.headers && !config.headers.originalHeaders) { + request.options.headers.originalHeaders = JSON.stringify(config.headers); + } + if (!currentUserToken) { + next(); + return; + } + request.options.headers.Authorization = currentUserToken; + next(); + } else { + // handle client use case separetly or else everything breaks + if (!currentUserToken) { + next(); + return; + } + if (!request.options.headers) { + request.options.headers = new Headers(); + } + request.options.headers.Authorization = currentUserToken; next(); - return; } - - if (!request.options.headers) { - request.options.headers = new Headers(); - } - - request.options.headers.Authorization = currentUserToken; - request.options.headers.locale = config.locale; - - next(); + }, }]); } @@ -94,7 +113,7 @@ const meteorClientConfig = networkInterfaceConfig => { } return null; }, - } + }; }; export const createApolloClient = options => { diff --git a/packages/vulcan-lib/lib/modules/callbacks.js b/packages/vulcan-lib/lib/modules/callbacks.js index f3453ff0d..871b54ec9 100644 --- a/packages/vulcan-lib/lib/modules/callbacks.js +++ b/packages/vulcan-lib/lib/modules/callbacks.js @@ -1,6 +1,13 @@ +import { Meteor } from 'meteor/meteor'; + import { debug } from './debug.js'; import { Utils } from './utils'; +/** + * @summary Format callback hook names + */ +export const formatHookName = hook => typeof hook === 'string' && hook.toLowerCase(); + /** * @summary A list of all registered callback hooks */ @@ -29,17 +36,19 @@ export const registerCallback = function (callback) { */ export const addCallback = function (hook, callback) { + const formattedHook = formatHookName(hook); + if (!callback.name) { // eslint-disable-next-line no-console - console.log(`// Warning! You are adding an unnamed callback to ${hook}. Please use the function foo () {} syntax.`); + console.log(`// Warning! You are adding an unnamed callback to ${formattedHook}. Please use the function foo () {} syntax.`); } // if callback array doesn't exist yet, initialize it - if (typeof Callbacks[hook] === 'undefined') { - Callbacks[hook] = []; + if (typeof Callbacks[formattedHook] === 'undefined') { + Callbacks[formattedHook] = []; } - Callbacks[hook].push(callback); + Callbacks[formattedHook].push(callback); }; /** @@ -48,45 +57,51 @@ export const addCallback = function (hook, callback) { * @param {string} functionName - The name of the function to remove */ export const removeCallback = function (hookName, callbackName) { - Callbacks[hookName] = _.reject(Callbacks[hookName], function (callback) { + const formattedHook = formatHookName(hookName); + Callbacks[formattedHook] = _.reject(Callbacks[formattedHook], function (callback) { return callback.name === callbackName; }); }; /** * @summary Successively run all of a hook's callbacks on an item - * @param {String} hook - First argument: the name of the hook + * @param {String} hook - First argument: the name of the hook, or an array * @param {Object} item - Second argument: the post, comment, modifier, etc. on which to run the callbacks * @param {Any} args - Other arguments will be passed to each successive iteration + * @param {Array} callbacks - Optionally, pass an array of callback functions instead of passing a hook name * @returns {Object} Returns the item after it's been through all the callbacks for this hook */ export const runCallbacks = function () { - let hook, item, args; + let hook, item, args, callbacks, formattedHook; if (typeof arguments[0] === 'object' && arguments.length === 1) { const singleArgument = arguments[0]; hook = singleArgument.name; + formattedHook = formatHookName(hook); item = singleArgument.iterator; args = singleArgument.properties; + // if callbacks option is passed used that, else use formatted hook name + callbacks = singleArgument.callbacks ? singleArgument.callbacks : Callbacks[formattedHook]; } else { // OpenCRUD backwards compatibility // the first argument is the name of the hook or an array of functions hook = arguments[0]; + formattedHook = formatHookName(hook); // the second argument is the item on which to iterate item = arguments[1]; // successive arguments are passed to each iteration args = Array.prototype.slice.call(arguments).slice(2); + // if first argument is an array, use that as callbacks array; else use formatted hook name + callbacks = Array.isArray(hook) ? hook : Callbacks[formattedHook]; } // flag used to detect the callback that initiated the async context let asyncContext = false; - - const callbacks = Array.isArray(hook) ? hook : Callbacks[hook]; - + if (typeof callbacks !== 'undefined' && !!callbacks.length) { // if the hook exists, and contains callbacks to run const runCallback = (accumulator, callback) => { - debug(`\x1b[32m>> Running callback [${callback.name}] on hook [${hook}]\x1b[0m`); + debug(`\x1b[32m>> Running callback [${callback.name}] on hook [${formattedHook}]\x1b[0m`); const newArguments = [accumulator].concat(args); try { @@ -94,7 +109,7 @@ export const runCallbacks = function () { // if callback is only supposed to run once, remove it if (callback.runOnce) { - removeCallback(hook, callback.name); + removeCallback(formattedHook, callback.name); } if (typeof result === 'undefined') { @@ -107,7 +122,7 @@ export const runCallbacks = function () { } catch (error) { // eslint-disable-next-line no-console - console.log(`\x1b[31m// error at callback [${callback.name}] in hook [${hook}]\x1b[0m`); + console.log(`\x1b[31m// error at callback [${callback.name}] in hook [${formattedHook}]\x1b[0m`); // eslint-disable-next-line no-console console.log(error); if (error.break || error.data && error.data.break) { @@ -121,7 +136,7 @@ export const runCallbacks = function () { return callbacks.reduce(function (accumulator, callback, index) { if (Utils.isPromise(accumulator)) { if (!asyncContext) { - debug(`\x1b[32m>> Started async context in hook [${hook}] by [${callbacks[index-1].name}]\x1b[0m`); + debug(`\x1b[32m>> Started async context in hook [${formattedHook}] by [${callbacks[index-1] && callbacks[index-1].name}]\x1b[0m`); asyncContext = true; } return new Promise((resolve, reject) => { @@ -169,17 +184,24 @@ export const runCallbacksAsync = function () { const callbacks = Array.isArray(hook) ? hook : Callbacks[hook]; - if (Meteor.isServer && typeof callbacks !== 'undefined' && !!callbacks.length) { + if (typeof callbacks !== 'undefined' && !!callbacks.length) { + const _runCallbacksAsync = () => + Promise.all( + callbacks.map(callback => { + debug(`\x1b[32m>> Running async callback [${callback.name}] on hook [${hook}]\x1b[0m`); + return callback.apply(this, args); + }), + ); - // use defer to avoid holding up client - Meteor.defer(function () { - // run all post submit server callbacks on post object successively - callbacks.forEach(function (callback) { - debug(`\x1b[32m>> Running async callback [${callback.name}] on hook [${hook}]\x1b[0m`); - callback.apply(this, args); + if (Meteor.isServer) { + // TODO: find out if we can safely use promises on the server, too - https://github.com/VulcanJS/Vulcan/pull/2065 + return new Promise(async (resolve, reject) => { + Meteor.defer(function() { + _runCallbacksAsync().then(resolve).catch(reject); + }); }); - }); - + } + return _runCallbacksAsync(); } - -}; \ No newline at end of file + return []; +}; diff --git a/packages/vulcan-lib/lib/modules/collections.js b/packages/vulcan-lib/lib/modules/collections.js index 0774c2e03..a9a9ed01e 100644 --- a/packages/vulcan-lib/lib/modules/collections.js +++ b/packages/vulcan-lib/lib/modules/collections.js @@ -2,13 +2,13 @@ import { Mongo } from 'meteor/mongo'; import SimpleSchema from 'simpl-schema'; import { addGraphQLCollection, addToGraphQLContext } from './graphql.js'; import { Utils } from './utils.js'; -import { runCallbacks, runCallbacksAsync } from './callbacks.js'; +import { runCallbacks, runCallbacksAsync, registerCallback, addCallback } from './callbacks.js'; import { getSetting, registerSetting } from './settings.js'; import { registerFragment, getDefaultFragmentText } from './fragments.js'; import escapeStringRegexp from 'escape-string-regexp'; -import { validateIntlField, getIntlString, isIntlField } from './intl'; +import { validateIntlField, getIntlString, isIntlField, schemaHasIntlFields } from './intl'; -const wrapAsync = (Meteor.wrapAsync)? Meteor.wrapAsync : Meteor._wrapAsync; +const wrapAsync = Meteor.wrapAsync ? Meteor.wrapAsync : Meteor._wrapAsync; // import { debug } from './debug.js'; registerSetting('maxDocumentsPerRequest', 1000, 'Maximum documents per request'); @@ -18,24 +18,27 @@ export let hasIntlFields = false; export const Collections = []; -export const getCollection = name => Collections.find(({ options: { collectionName }}) => name === collectionName || name === collectionName.toLowerCase()); +export const getCollection = name => + Collections.find( + ({ options: { collectionName } }) => name === collectionName || name === collectionName.toLowerCase() + ); // TODO: find more reliable way to get collection name from type name? export const getCollectionName = typeName => Utils.pluralize(typeName); // TODO: find more reliable way to get type name from collection name? -export const getTypeName = collectionName => collectionName.slice(0,-1); +export const getTypeName = collectionName => collectionName.slice(0, -1); /** * @summary replacement for Collection2's attachSchema. Pass either a schema, to * initialize or replace the schema, or some fields, to extend the current schema * @class Mongo.Collection */ -Mongo.Collection.prototype.attachSchema = function (schemaOrFields) { +Mongo.Collection.prototype.attachSchema = function(schemaOrFields) { if (schemaOrFields instanceof SimpleSchema) { this.simpleSchema = () => schemaOrFields; } else { - this.simpleSchema().extend(schemaOrFields) + this.simpleSchema().extend(schemaOrFields); } }; @@ -43,18 +46,15 @@ Mongo.Collection.prototype.attachSchema = function (schemaOrFields) { * @summary Add an additional field (or an array of fields) to a schema. * @param {Object|Object[]} field */ -Mongo.Collection.prototype.addField = function (fieldOrFieldArray) { - +Mongo.Collection.prototype.addField = function(fieldOrFieldArray) { const collection = this; - const schema = collection.simpleSchema()._schema; const fieldSchema = {}; const fieldArray = Array.isArray(fieldOrFieldArray) ? fieldOrFieldArray : [fieldOrFieldArray]; // loop over fields and add them to schema (or extend existing fields) - fieldArray.forEach(function (field) { - const newField = {...schema[field.fieldName], ...field.fieldSchema}; - fieldSchema[field.fieldName] = newField; + fieldArray.forEach(function(field) { + fieldSchema[field.fieldName] = field.fieldSchema; }); // add field schema to collection schema @@ -65,8 +65,7 @@ Mongo.Collection.prototype.addField = function (fieldOrFieldArray) { * @summary Remove a field from a schema. * @param {String} fieldName */ -Mongo.Collection.prototype.removeField = function (fieldName) { - +Mongo.Collection.prototype.removeField = function(fieldName) { var collection = this; var schema = _.omit(collection.simpleSchema()._schema, fieldName); @@ -78,7 +77,7 @@ Mongo.Collection.prototype.removeField = function (fieldName) { * @summary Add a default view function. * @param {Function} view */ -Mongo.Collection.prototype.addDefaultView = function (view) { +Mongo.Collection.prototype.addDefaultView = function(view) { this.defaultView = view; }; @@ -87,16 +86,16 @@ Mongo.Collection.prototype.addDefaultView = function (view) { * @param {String} viewName * @param {Function} view */ -Mongo.Collection.prototype.addView = function (viewName, view) { +Mongo.Collection.prototype.addView = function(viewName, view) { this.views[viewName] = view; }; /** * @summary Allow mongodb aggregation * @param {Array} pipelines mongodb pipeline - * @param {Object} options mongodb option object + * @param {Object} options mongodb option object */ -Mongo.Collection.prototype.aggregate = function (pipelines, options) { +Mongo.Collection.prototype.aggregate = function(pipelines, options) { var coll = this.rawCollection(); return wrapAsync(coll.aggregate.bind(coll))(pipelines, options); }; @@ -106,11 +105,12 @@ Mongo.Collection.prototype.helpers = function(helpers) { var self = this; if (self._transform && !self._helpers) - throw new Meteor.Error('Can\'t apply helpers to \'' + - self._name + '\' a transform function already exists!'); + throw new Meteor.Error('Can\'t apply helpers to \'' + self._name + '\' a transform function already exists!'); if (!self._helpers) { - self._helpers = function Document(doc) { return _.extend(this, doc); }; + self._helpers = function Document(doc) { + return _.extend(this, doc); + }; self._transform = function(doc) { return new self._helpers(doc); }; @@ -122,12 +122,20 @@ Mongo.Collection.prototype.helpers = function(helpers) { }; export const createCollection = options => { - - const { typeName, collectionName = getCollectionName(typeName), schema, generateGraphQLSchema = true, dbCollectionName } = options; + const { + typeName, + collectionName = getCollectionName(typeName), + generateGraphQLSchema = true, + dbCollectionName + } = options; + let { schema } = options; // initialize new Mongo collection - const collection = collectionName === 'Users' && Meteor.users ? Meteor.users : new Mongo.Collection(dbCollectionName ? dbCollectionName : collectionName.toLowerCase()); - + const collection = + collectionName === 'Users' && Meteor.users + ? Meteor.users + : new Mongo.Collection(dbCollectionName ? dbCollectionName : collectionName.toLowerCase()); + // decorate collection with options collection.options = options; @@ -140,45 +148,23 @@ export const createCollection = options => { // add collectionName if missing collection.collectionName = collectionName; collection.options.collectionName = collectionName; - + // add views collection.views = []; - // generate foo_intl fields - Object.keys(schema).forEach(fieldName => { - const fieldSchema = schema[fieldName]; - if (isIntlField(fieldSchema)) { + //register individual collection callback + registerCollectionCallback(typeName.toLowerCase()); - // we have at least one intl field - hasIntlFields = true; + // if schema has at least one intl field, add intl callback just before + // `${collectionName}.collection` callbacks run to make sure it always runs last + if (schemaHasIntlFields(schema)) { + hasIntlFields = true; // we have at least one intl field + addCallback(`${typeName.toLowerCase()}.collection`, addIntlFields); + } - // remove `intl` to avoid treating new _intl field as a field to internationalize - // eslint-disable-next-line no-unused-vars - const { intl, ...propertiesToCopy } = schema[fieldName]; - - schema[`${fieldName}_intl`] = { - ...propertiesToCopy, // copy properties from regular field - hidden: true, - type: Array, - isIntlData: true, - }; - - delete schema[`${fieldName}_intl`].intl; - - schema[`${fieldName}_intl.$`] = { - type: getIntlString(), - }; - - // if original field is required, enable custom validation function instead of `optional` property - if (!schema[fieldName].optional) { - schema[`${fieldName}_intl`].optional = true; - schema[`${fieldName}_intl`].custom = validateIntlField; - } - - // make original non-intl field optional - schema[fieldName].optional = true; - } - }); + //run schema callbacks and run general callbacks last + schema = runCallbacks({ name: `${typeName.toLowerCase()}.collection`, iterator: schema, properties: { options }}); + schema = runCallbacks({ name: '*.collection', iterator: schema, properties: { options }}); if (schema) { // attach schema to collection @@ -190,13 +176,13 @@ export const createCollection = options => { context[collectionName] = collection; addToGraphQLContext(context); - if (generateGraphQLSchema){ + if (generateGraphQLSchema) { // add collection to list of dynamically generated GraphQL schemas addGraphQLCollection(collection); } - runCallbacksAsync({ name: '*.collection', properties: { options } }); - runCallbacksAsync({ name: `${collectionName}.collection`, properties: { options } }); + runCallbacksAsync({ name: '*.collection.async', properties: { options } }); + runCallbacksAsync({ name: `${collectionName}.collection.async`, properties: { options } }); // ------------------------------------- Default Fragment -------------------------------- // @@ -206,7 +192,6 @@ export const createCollection = options => { // ------------------------------------- Parameters -------------------------------- // collection.getParameters = (terms = {}, apolloClient, context) => { - // console.log(terms); let parameters = { @@ -215,7 +200,7 @@ export const createCollection = options => { }; if (collection.defaultView) { - parameters = Utils.deepExtend(true, parameters, collection.defaultView(terms, apolloClient)); + parameters = Utils.deepExtend(true, parameters, collection.defaultView(terms, apolloClient, context)); } // handle view option @@ -225,26 +210,53 @@ export const createCollection = options => { } // iterate over posts.parameters callbacks - parameters = runCallbacks(`${typeName.toLowerCase()}.parameters`, parameters, _.clone(terms), apolloClient, context); + parameters = runCallbacks( + `${typeName.toLowerCase()}.parameters`, + parameters, + _.clone(terms), + apolloClient, + context + ); // OpenCRUD backwards compatibility - parameters = runCallbacks(`${collectionName.toLowerCase()}.parameters`, parameters, _.clone(terms), apolloClient, context); + parameters = runCallbacks( + `${collectionName.toLowerCase()}.parameters`, + parameters, + _.clone(terms), + apolloClient, + context + ); if (Meteor.isClient) { - parameters = runCallbacks(`${typeName.toLowerCase()}.parameters.client`, parameters, _.clone(terms), apolloClient); + parameters = runCallbacks( + `${typeName.toLowerCase()}.parameters.client`, + parameters, + _.clone(terms), + apolloClient + ); // OpenCRUD backwards compatibility - parameters = runCallbacks(`${collectionName.toLowerCase()}.parameters.client`, parameters, _.clone(terms), apolloClient); + parameters = runCallbacks( + `${collectionName.toLowerCase()}.parameters.client`, + parameters, + _.clone(terms), + apolloClient + ); } // note: check that context exists to avoid calling this from withList during SSR if (Meteor.isServer && context) { parameters = runCallbacks(`${typeName.toLowerCase()}.parameters.server`, parameters, _.clone(terms), context); // OpenCRUD backwards compatibility - parameters = runCallbacks(`${collectionName.toLowerCase()}.parameters.server`, parameters, _.clone(terms), context); + parameters = runCallbacks( + `${collectionName.toLowerCase()}.parameters.server`, + parameters, + _.clone(terms), + context + ); } // sort using terms.orderBy (overwrite defaultView's sort) if (terms.orderBy && !_.isEmpty(terms.orderBy)) { - parameters.options.sort = terms.orderBy + parameters.options.sort = terms.orderBy; } // if there is no sort, default to sorting by createdAt descending @@ -255,7 +267,7 @@ export const createCollection = options => { // extend sort to sort posts by _id to break ties, unless there's already an id sort // NOTE: always do this last to avoid overriding another sort if (!(parameters.options.sort && parameters.options.sort._id)) { - parameters = Utils.deepExtend(true, parameters, {options: {sort: {_id: -1}}}); + parameters = Utils.deepExtend(true, parameters, { options: { sort: { _id: -1 } } }); } // remove any null fields (setting a field to null means it should be deleted) @@ -269,23 +281,29 @@ export const createCollection = options => { } if (terms.query) { - const query = escapeStringRegexp(terms.query); - const currentSchema = collection.simpleSchema()._schema; + const currentSchema = collection.simpleSchema()._schema; const searchableFieldNames = _.filter(_.keys(currentSchema), fieldName => currentSchema[fieldName].searchable); if (searchableFieldNames.length) { parameters = Utils.deepExtend(true, parameters, { selector: { - $or: searchableFieldNames.map(fieldName => ({[fieldName]: {$regex: query, $options: 'i'}})) + $or: searchableFieldNames.map(fieldName => ({ [fieldName]: { $regex: query, $options: 'i' } })) } }); + } else { + // eslint-disable-next-line no-console + console.warn( + `Warning: terms.query is set but schema ${ + collection.options.typeName + } has no searchable field. Set "searchable: true" for at least one field to enable search.` + ); } } // limit number of items to 1000 by default const maxDocuments = getSetting('maxDocumentsPerRequest', 1000); const limit = terms.limit || parameters.options.limit; - parameters.options.limit = (!limit || limit < 1 || limit > maxDocuments) ? maxDocuments : limit; + parameters.options.limit = !limit || limit < 1 || limit > maxDocuments ? maxDocuments : limit; // console.log(parameters); @@ -296,3 +314,67 @@ export const createCollection = options => { return collection; }; + +//register collection creation hook for each collection +function registerCollectionCallback(typeName) { + registerCallback({ + name: `${typeName}.collection`, + iterator: { schema: 'the schema of the collection' }, + properties: [ + { schema: 'The schema of the collection' }, + { validationErrors: 'An Object that can be used to accumulate validation errors' } + ], + runs: 'sync', + returns: 'schema', + description: 'Modifies schemas on collection creation' + }); +} + +//register colleciton creation hook +registerCallback({ + name: '*.collection', + iterator: { schema: 'the schema of the collection' }, + properties: [ + { schema: 'The schema of the collection' }, + { validationErrors: 'An object that can be used to accumulate validation errors' } + ], + runs: 'sync', + returns: 'schema', + description: 'Modifies schemas on collection creation', +}); + +// generate foo_intl fields +function addIntlFields(schema) { + Object.keys(schema).forEach(fieldName => { + const fieldSchema = schema[fieldName]; + if (isIntlField(fieldSchema)) { + + // remove `intl` to avoid treating new _intl field as a field to internationalize + // eslint-disable-next-line no-unused-vars + const { intl, ...propertiesToCopy } = schema[fieldName]; + + schema[`${fieldName}_intl`] = { + ...propertiesToCopy, // copy properties from regular field + hidden: true, + type: Array, + isIntlData: true + }; + + delete schema[`${fieldName}_intl`].intl; + + schema[`${fieldName}_intl.$`] = { + type: getIntlString() + }; + + // if original field is required, enable custom validation function instead of `optional` property + if (!schema[fieldName].optional) { + schema[`${fieldName}_intl`].optional = true; + schema[`${fieldName}_intl`].custom = validateIntlField; + } + + // make original non-intl field optional + schema[fieldName].optional = true; + } + }); + return schema; +} diff --git a/packages/vulcan-lib/lib/modules/components.js b/packages/vulcan-lib/lib/modules/components.js index 2651150e8..a4e5430f7 100644 --- a/packages/vulcan-lib/lib/modules/components.js +++ b/packages/vulcan-lib/lib/modules/components.js @@ -2,7 +2,7 @@ import { compose } from 'react-apollo'; // note: at the moment, compose@react-ap import React from 'react'; export const Components = {}; // will be populated on startup (see vulcan:routing) -export const ComponentsTable = {} // storage for infos about components +export const ComponentsTable = {}; // storage for infos about components /** * Register a Vulcan component with a name, a raw component than can be extended @@ -33,14 +33,14 @@ export function registerComponent(name, rawComponent, ...hocs) { // as arguments so destructuring cannot work // eslint-disable-next-line no-redeclare var { name, component, hocs = [] } = arguments[0]; - rawComponent = component + rawComponent = component; } // store the component in the table ComponentsTable[name] = { name, rawComponent, hocs, - } + }; } /** @@ -52,7 +52,7 @@ export function registerComponent(name, rawComponent, ...hocs) { export const getComponent = (name) => { const component = ComponentsTable[name]; if (!component) { - throw new Error(`Component ${name} not registered.`) + throw new Error(`Component ${name} not registered.`); } if (component.hocs) { const hocs = component.hocs.map(hoc => { @@ -80,7 +80,7 @@ export const populateComponentsApp = () => { // uncomment for debug // console.log('init component:', name); }); -} +}; /** * Get the **raw** (original) component registered with registerComponent @@ -136,7 +136,7 @@ export const populateComponentsApp = () => { export const copyHoCs = (sourceComponent, targetComponent) => { return compose(...sourceComponent.hocs)(targetComponent); -} +}; /** * Returns an instance of the given component name of function @@ -149,10 +149,10 @@ export const instantiateComponent = (component, props) => { return null; } else if (typeof component === 'string') { const Component = getComponent(component); - return + return ; } else if (typeof component === 'function' && component.prototype && component.prototype.isReactComponent) { const Component = component; - return + return ; } else if (typeof component === 'function') { return component(props); } else { @@ -189,3 +189,17 @@ export const delayedComponent = name => { return Component && ; }; }; + + +// Example with Proxy (might be unstable/hard to reason about) +//const mergeWithComponents = (myComponents = {}) => { +// const handler = { +// get: function(target, name) { +// return name in target ? target[name] : Components[name]; +// } +// }; +// const proxy = new Proxy(myComponents, handler); +// return proxy; +//}; +export const mergeWithComponents = myComponents => (myComponents ? { ...Components, ...myComponents } : Components); + diff --git a/packages/vulcan-lib/lib/modules/config.js b/packages/vulcan-lib/lib/modules/config.js index 62f2ceaba..5ffe486b9 100644 --- a/packages/vulcan-lib/lib/modules/config.js +++ b/packages/vulcan-lib/lib/modules/config.js @@ -9,7 +9,7 @@ import SimpleSchema from 'simpl-schema'; Vulcan = {}; // eslint-disable-next-line no-undef -Vulcan.VERSION = '1.12.8'; +Vulcan.VERSION = '1.12.13'; // ------------------------------------- Schemas -------------------------------- // diff --git a/packages/vulcan-lib/lib/modules/debug.js b/packages/vulcan-lib/lib/modules/debug.js index 15c261dae..45b6b3ea9 100644 --- a/packages/vulcan-lib/lib/modules/debug.js +++ b/packages/vulcan-lib/lib/modules/debug.js @@ -5,17 +5,17 @@ export const debug = function () { // eslint-disable-next-line no-console console.log.apply(null, arguments); } -} +}; export const debugGroup = function () { if (getSetting('debug', false)) { // eslint-disable-next-line no-console console.groupCollapsed.apply(null, arguments); } -} +}; export const debugGroupEnd = function () { if (getSetting('debug', false)) { // eslint-disable-next-line no-console console.groupEnd.apply(null, arguments); } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/packages/vulcan-lib/lib/modules/detect_locale.js b/packages/vulcan-lib/lib/modules/detect_locale.js index a9220bb8f..37c07b9d2 100644 --- a/packages/vulcan-lib/lib/modules/detect_locale.js +++ b/packages/vulcan-lib/lib/modules/detect_locale.js @@ -4,7 +4,7 @@ Helper to detect current browser locale */ export const detectLocale = () => { - var lang + var lang; if (typeof navigator === 'undefined') { return null; @@ -12,14 +12,14 @@ export const detectLocale = () => { if (navigator.languages && navigator.languages.length) { // latest versions of Chrome and Firefox set this correctly - lang = navigator.languages[0] + lang = navigator.languages[0]; } else if (navigator.userLanguage) { // IE only - lang = navigator.userLanguage + lang = navigator.userLanguage; } else { // latest versions of Chrome, Firefox, and Safari set this correctly - lang = navigator.language + lang = navigator.language; } - return lang -} \ No newline at end of file + return lang; +}; \ No newline at end of file diff --git a/packages/vulcan-lib/lib/modules/errors.js b/packages/vulcan-lib/lib/modules/errors.js index 219f5bd63..4a37a728c 100644 --- a/packages/vulcan-lib/lib/modules/errors.js +++ b/packages/vulcan-lib/lib/modules/errors.js @@ -1,3 +1,5 @@ +import { createError } from 'apollo-errors'; + /* Get whatever word is contained between the first two double quotes @@ -9,7 +11,7 @@ const getFirstWord = input => { return null; } return parts[1]; -} +}; /* @@ -40,16 +42,16 @@ const parseErrorMessage = message => { properties: { name: fieldName, }, - } + }; } else { // other generic GraphQL errors return { message: error - } + }; } }); return fieldErrors; -} +}; /* Errors can have the following properties stored on their `data` property: @@ -89,4 +91,19 @@ export const getErrors = error => { } } return errors; -} \ No newline at end of file +}; + +/* + +An error should have: + +- id: will be used as i18n key (note: available as `name` on the client) +- message: optionally, a plain-text message +- data: data/values to give more context to the error + +*/ +export const throwError = error => { + const { id, message = id, data } = error; + const MissingDocumentError = createError(id, { message }); + throw new MissingDocumentError({ id, data }); +}; \ No newline at end of file diff --git a/packages/vulcan-lib/lib/modules/findbyids.js b/packages/vulcan-lib/lib/modules/findbyids.js index e9b067a1a..d23656ff5 100644 --- a/packages/vulcan-lib/lib/modules/findbyids.js +++ b/packages/vulcan-lib/lib/modules/findbyids.js @@ -12,6 +12,6 @@ const findByIds = async function(collection, ids, context) { const orderedDocuments = ids.map(id => _.findWhere(documents, {_id: id})); return orderedDocuments; -} +}; export default findByIds; \ No newline at end of file diff --git a/packages/vulcan-lib/lib/modules/fragment_matcher.js b/packages/vulcan-lib/lib/modules/fragment_matcher.js index b4a4ed278..846eec83a 100644 --- a/packages/vulcan-lib/lib/modules/fragment_matcher.js +++ b/packages/vulcan-lib/lib/modules/fragment_matcher.js @@ -4,7 +4,7 @@ export const FragmentMatcher = []; export const addToFragmentMatcher = fragmentMatcher => { FragmentMatcher.push(fragmentMatcher); -} +}; export const getFragmentMatcher = () => { const fm = { @@ -15,4 +15,4 @@ export const getFragmentMatcher = () => { } }; return new IntrospectionFragmentMatcher(fm); -} +}; diff --git a/packages/vulcan-lib/lib/modules/fragments.js b/packages/vulcan-lib/lib/modules/fragments.js index 9b5562027..06f2d6eab 100644 --- a/packages/vulcan-lib/lib/modules/fragments.js +++ b/packages/vulcan-lib/lib/modules/fragments.js @@ -37,7 +37,7 @@ export const registerFragment = fragmentTextSource => { // register fragment Fragments[fragmentName] = { fragmentText - } + }; // also add subfragments if there are any if(subFragments && subFragments.length) { @@ -65,7 +65,7 @@ export const getFragmentObject = (fragmentText, subFragments) => { })] : [literals]; return gql.apply(null, gqlArguments); -} +}; /* @@ -92,7 +92,7 @@ export const getDefaultFragmentText = (collection, options = { onlyViewable: tru const fragmentText = ` fragment ${collection.options.collectionName}DefaultFragment on ${collection.typeName} { ${fieldNames.map(fieldName => { - return fieldName+'\n' + return fieldName+'\n'; }).join('')} } `; @@ -102,11 +102,11 @@ export const getDefaultFragmentText = (collection, options = { onlyViewable: tru return null; } -} +}; export const getDefaultFragment = collection => { const fragmentText = getDefaultFragmentText(collection); return fragmentText ? gql`${fragmentText}` : null; -} +}; /* Queue a fragment to be extended with additional properties. @@ -116,7 +116,7 @@ Note: can be used even before the fragment has been registered. */ export const extendFragment = (fragmentName, newProperties) => { FragmentsExtensions[fragmentName] = FragmentsExtensions[fragmentName] ? [...FragmentsExtensions[fragmentName], newProperties] : [newProperties]; -} +}; /* @@ -135,7 +135,7 @@ export const extendFragmentWithProperties = (fragmentName, newProperties) => { fragment.fragmentText.slice(fragmentEndPosition) ].join(''); registerFragment(newFragmentText); -} +}; /* @@ -148,7 +148,7 @@ export const removeFromFragment = (fragmentName, propertyName) => { const fragment = Fragments[fragmentName]; const newFragmentText = fragment.fragmentText.replace(propertyName, ''); registerFragment(newFragmentText); -} +}; /* @@ -171,7 +171,7 @@ export const getFragment = fragmentName => { } // return fragment object created by gql return Fragments[fragmentName].fragmentObject; -} +}; /* @@ -180,11 +180,11 @@ Get gql fragment text */ export const getFragmentText = fragmentName => { if (!Fragments[fragmentName]) { - throw new Error(`Fragment "${fragmentName}" not registered.`) + throw new Error(`Fragment "${fragmentName}" not registered.`); } // return fragment object created by gql return Fragments[fragmentName].fragmentText; -} +}; /* @@ -218,7 +218,7 @@ export const initializeFragments = (fragments = getNonInitializedFragmentNames() const keysWithoutSubFragments = _.filter(fragments, fragmentName => !Fragments[fragmentName].subFragments); _.forEach(keysWithoutSubFragments, fragmentName => { const fragment = Fragments[fragmentName]; - fragment.fragmentObject = getFragmentObject(fragment.fragmentText, fragment.subFragments) + fragment.fragmentObject = getFragmentObject(fragment.fragmentText, fragment.subFragments); }); // next, initialize fragments that *have* subfragments @@ -240,4 +240,4 @@ export const initializeFragments = (fragments = getNonInitializedFragmentNames() fragment.fragmentObject = getFragmentObject(fragment.fragmentText, fragment.subFragments); }); -} \ No newline at end of file +}; \ No newline at end of file diff --git a/packages/vulcan-lib/lib/modules/graphql.js b/packages/vulcan-lib/lib/modules/graphql.js index 17174679d..2079970fa 100644 --- a/packages/vulcan-lib/lib/modules/graphql.js +++ b/packages/vulcan-lib/lib/modules/graphql.js @@ -62,7 +62,7 @@ const getGraphQLType = (schema, fieldName, isInput = false) => { default: return null; } -} +}; export const GraphQLSchema = { @@ -174,7 +174,13 @@ export const GraphQLSchema = { // then build actual resolver object and pass it to addGraphQLResolvers const resolver = { [typeName]: { - [resolverName]: field.resolveAs.resolver + [resolverName]: (document, args, context, info) => { + const { Users, currentUser } = context; + // check that current user has permission to access the original non-resolved field + const canReadField = Users.canReadField(currentUser, field, document); + return canReadField ? field.resolveAs.resolver(document, args, context, info) : null; + + } } }; addGraphQLResolvers(resolver); @@ -265,7 +271,7 @@ export const GraphQLSchema = { const { interfaces = [], resolvers, mutations } = collection.options; - const description = collection.options.description ? collection.options.description : `Type for ${collectionName}` + const description = collection.options.description ? collection.options.description : `Type for ${collectionName}`; const { mainType, create, update, selector, selectorUnique, orderBy } = fields; @@ -355,7 +361,7 @@ export const GraphQLSchema = { } else { // eslint-disable-next-line no-console - console.log(`// Warning: collection ${collectionName} doesn't have any GraphQL-enabled fields, so no corresponding type can be generated. Pass generateGraphQLSchema = false to createCollection() to disable this warning`) + console.log(`// Warning: collection ${collectionName} doesn't have any GraphQL-enabled fields, so no corresponding type can be generated. Pass generateGraphQLSchema = false to createCollection() to disable this warning`); } return graphQLSchema; @@ -385,7 +391,7 @@ Vulcan.getGraphQLSchema = () => { // eslint-disable-next-line no-console // console.log(schema); return schema; -} +}; export const addGraphQLCollection = GraphQLSchema.addCollection.bind(GraphQLSchema); export const addGraphQLSchema = GraphQLSchema.addSchema.bind(GraphQLSchema); diff --git a/packages/vulcan-core/lib/modules/containers/handleOptions.js b/packages/vulcan-lib/lib/modules/handleOptions.js similarity index 82% rename from packages/vulcan-core/lib/modules/containers/handleOptions.js rename to packages/vulcan-lib/lib/modules/handleOptions.js index 4c9581d4e..2399130fa 100644 --- a/packages/vulcan-core/lib/modules/containers/handleOptions.js +++ b/packages/vulcan-lib/lib/modules/handleOptions.js @@ -1,4 +1,10 @@ -import { getFragment, getCollection, getFragmentName } from 'meteor/vulcan:core'; +/** Helpers to get values depending on name + * E.g. retrieving a collection and its name when only one value is provided + * + */ + +import { getCollection } from './collections'; +import { getFragment, getFragmentName } from './fragments'; /** * Extract collectionName from collection * or collection from collectionName diff --git a/packages/vulcan-lib/lib/modules/headtags.js b/packages/vulcan-lib/lib/modules/headtags.js index 18348d49c..9a052b896 100644 --- a/packages/vulcan-lib/lib/modules/headtags.js +++ b/packages/vulcan-lib/lib/modules/headtags.js @@ -3,12 +3,12 @@ export const Head = { link: [], script: [], components: [], -} +}; export const removeFromHeadTags = (type, name)=>{ Head[type] = Head[type].filter((tag)=>{ - return (!tag.name || tag.name && tag.name !== name) + return (!tag.name || tag.name && tag.name !== name); }); return Head; -} +}; diff --git a/packages/vulcan-lib/lib/modules/index.js b/packages/vulcan-lib/lib/modules/index.js index 81a28bab7..2269725f0 100644 --- a/packages/vulcan-lib/lib/modules/index.js +++ b/packages/vulcan-lib/lib/modules/index.js @@ -15,10 +15,9 @@ export * from './graphql.js'; export * from './routes.js'; export * from './utils.js'; export * from './settings.js'; -export * from './strings.js'; export * from './headtags.js'; export * from './fragments.js'; -export * from './apollo-common' +export * from './apollo-common'; export * from './dynamic_loader.js'; export * from './admin.js'; export * from './fragment_matcher.js'; @@ -29,5 +28,5 @@ export * from './intl.js'; export * from './detect_locale.js'; export * from './graphql_templates.js'; export * from './validation.js'; -// export * from './apollo-client/updates.js'; +export * from './handleOptions'; // export * from './resolvers.js'; diff --git a/packages/vulcan-lib/lib/modules/intl.js b/packages/vulcan-lib/lib/modules/intl.js index 4ff117895..caf5ee114 100644 --- a/packages/vulcan-lib/lib/modules/intl.js +++ b/packages/vulcan-lib/lib/modules/intl.js @@ -1,10 +1,40 @@ import SimpleSchema from 'simpl-schema'; +export const Strings = {}; + +export const Domains = {}; + +export const addStrings = (language, strings) => { + if (typeof Strings[language] === 'undefined') { + Strings[language] = {}; + } + Strings[language] = { + ...Strings[language], + ...strings + }; +}; + +export const getString = ({id, values, defaultMessage, locale}) => { + const messages = Strings[locale] || {}; + let message = messages[id] || defaultMessage; + if (message && values) { + Object.keys(values).forEach(key => { + // note: see replaceAll definition in vulcan:lib/utils + message = message.replaceAll(`{${key}}`, values[key]); + }); + } + return message; +}; + +export const registerDomain = (locale, domain) => { + Domains[domain] = locale; +}; + export const Locales = []; export const registerLocale = locale => { Locales.push(locale); -} +}; /* @@ -14,7 +44,7 @@ Note: look into simplifying this */ export const isIntlField = fieldSchema => { return fieldSchema.intl; -} +}; /* @@ -22,22 +52,28 @@ Generate custom IntlString SimpleSchema type */ export const getIntlString = () => { - const schema = { locale: { type: String, - optional: true, + optional: true }, value: { type: String, - optional: true, + optional: true } }; const IntlString = new SimpleSchema(schema); IntlString.name = 'IntlString'; return IntlString; -} +}; + +/* + +Check if a schema has at least one intl field + +*/ +export const schemaHasIntlFields = schema => Object.keys(schema).some(fieldName => isIntlField(schema[fieldName])); /* @@ -46,7 +82,7 @@ Custom validation function to check for required locales See https://github.com/aldeed/simple-schema-js#custom-field-validation */ -export const validateIntlField = function () { +export const validateIntlField = function() { let errors = []; // go through locales to check which one are required @@ -54,10 +90,14 @@ export const validateIntlField = function () { requiredLocales.forEach((locale, index) => { const strings = this.value; - const hasString = strings && strings.some(s => s && s.locale === locale.id && s.value); + const hasString = strings && Array.isArray(strings) && strings.some(s => s && s.locale === locale.id && s.value); if (!hasString) { const originalFieldName = this.key.replace('_intl', ''); - errors.push({ id: 'errors.required', path: `${this.key}.${index}`, properties: { name: originalFieldName, locale: locale.id }}); + errors.push({ + id: 'errors.required', + path: `${this.key}.${index}`, + properties: { name: originalFieldName, locale: locale.id } + }); } }); @@ -65,4 +105,4 @@ export const validateIntlField = function () { // hack to work around the fact that custom validation function can only return a single string return `intlError|${JSON.stringify(errors)}`; } -} \ No newline at end of file +}; diff --git a/packages/vulcan-lib/lib/modules/mongo_redux.js b/packages/vulcan-lib/lib/modules/mongo_redux.js index 8eb29a427..96741e419 100644 --- a/packages/vulcan-lib/lib/modules/mongo_redux.js +++ b/packages/vulcan-lib/lib/modules/mongo_redux.js @@ -4,7 +4,7 @@ import Mingo from 'mingo'; Mongo.Collection.prototype.findInStore = function (store, selector = {}, options = {}) { const typeName = this.options && this.options.typeName; - const docs = _.where(store.getState().apollo.data, {__typename: typeName}) + const docs = _.where(store.getState().apollo.data, {__typename: typeName}); const mingoQuery = new Mingo.Query(selector); @@ -20,9 +20,9 @@ Mongo.Collection.prototype.findInStore = function (store, selector = {}, options // console.log("sorted docs: ", cursor.sort(options.sort).all()) return {fetch: () => sortedDocs}; -} +}; Mongo.Collection.prototype.findOneInStore = function (store, _idOrObject) { const docs = typeof _idOrObject === 'string' ? this.findInStore(store, {_id: _idOrObject}).fetch() : this.findInStore(store, _idOrObject).fetch(); return docs.length === 0 ? undefined: docs[0]; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/packages/vulcan-lib/lib/modules/routes.js b/packages/vulcan-lib/lib/modules/routes.js index 6352108c9..a388c6eb6 100644 --- a/packages/vulcan-lib/lib/modules/routes.js +++ b/packages/vulcan-lib/lib/modules/routes.js @@ -78,7 +78,7 @@ export const addAsChildRoute = (parentRouteName, addedRoutes) => { // if the parentRouteName does not exist, error if (!RoutesTable[parentRouteName]) { - throw new Error(`Route ${parentRouteName} doesn't exist`) + throw new Error(`Route ${parentRouteName} doesn't exist`); } // modify the routes table with the new routes @@ -118,7 +118,7 @@ export const getRoute = name => { } return routeDef; -} +}; export const getChildRoute = (name, index) => { const routeDef = RoutesTable[name]['childRoutes'][index]; @@ -129,7 +129,7 @@ export const getChildRoute = (name, index) => { } return routeDef; -} +}; /** * Populate the lookup table for routes to be callable @@ -151,5 +151,5 @@ export const populateRoutesApp = () => { // uncomment for debug // console.log('init route:', name); }); -} +}; diff --git a/packages/vulcan-lib/lib/modules/settings.js b/packages/vulcan-lib/lib/modules/settings.js index 28f52a467..4d4a4da64 100644 --- a/packages/vulcan-lib/lib/modules/settings.js +++ b/packages/vulcan-lib/lib/modules/settings.js @@ -53,16 +53,16 @@ export const getAllSettings = () => { }); return _.map(settingsObject, (setting, key) => ({name: key, ...setting})); -} +}; Vulcan.showSettings = () => { return getAllSettings(); -} +}; export const registerSetting = (settingName, defaultValue, description, isPublic) => { Settings[settingName] = { defaultValue, description, isPublic }; -} +}; export const getSetting = (settingName, settingDefault) => { @@ -84,7 +84,7 @@ export const getSetting = (settingName, settingDefault) => { ...rootSetting, ...privateSetting, ...publicSetting, - } + }; } else { if (typeof rootSetting !== 'undefined') { setting = rootSetting; @@ -107,6 +107,6 @@ export const getSetting = (settingName, settingDefault) => { return setting; -} +}; registerSetting('debug', false, 'Enable debug mode (more verbose logging)'); diff --git a/packages/vulcan-lib/lib/modules/strings.js b/packages/vulcan-lib/lib/modules/strings.js deleted file mode 100644 index dc086f65a..000000000 --- a/packages/vulcan-lib/lib/modules/strings.js +++ /dev/null @@ -1,17 +0,0 @@ -export const Strings = {}; - -export const Domains = {}; - -export const addStrings = (language, strings) => { - if (typeof Strings[language] === 'undefined') { - Strings[language] = {}; - } - Strings[language] = { - ...Strings[language], - ...strings - }; -}; - -export const registerDomain = (locale, domain) => { - Domains[domain] = locale; -} \ No newline at end of file diff --git a/packages/vulcan-lib/lib/modules/utils.js b/packages/vulcan-lib/lib/modules/utils.js index 6b353bd50..0f5ad220c 100644 --- a/packages/vulcan-lib/lib/modules/utils.js +++ b/packages/vulcan-lib/lib/modules/utils.js @@ -14,6 +14,7 @@ import { getCollection } from './collections.js'; import set from 'lodash/set'; import get from 'lodash/get'; import isFunction from 'lodash/isFunction'; +import { throwError } from './errors.js'; registerSetting('debug', false, 'Enable debug mode (more verbose logging)'); @@ -168,7 +169,6 @@ Utils.slugify = function (s) { return slug; }; - Utils.getUnusedSlug = function (collection, slug) { let suffix = ''; let index = 0; @@ -182,6 +182,23 @@ Utils.getUnusedSlug = function (collection, slug) { return slug+suffix; }; +// Different version, less calls to the db but it cannot be used until we figure out how to use async for onCreate functions +// Utils.getUnusedSlug = async function (collection, slug) { +// let suffix = ''; +// let index = 0; +// +// const slugRegex = new RegExp('^' + slug + '-[0-9]+$'); +// // get all the slugs matching slug or slug-123 in that collection +// const results = await collection.find( { slug: { $in: [slug, slugRegex] } }, { fields: { slug: 1, _id: 0 } }); +// const usedSlugs = results.map(item => item.slug); +// // increment the index at the end of the slug until we find an unused one +// while (usedSlugs.indexOf(slug + suffix) !== -1) { +// index++; +// suffix = '-' + index; +// } +// return slug + suffix; +// }; + Utils.getUnusedSlugByCollectionName = function (collectionName, slug) { return Utils.getUnusedSlug(getCollection(collectionName), slug); }; @@ -275,7 +292,7 @@ _.mixin({ */ if (typeof value === 'boolean' || typeof value === 'number') { - return + return; } if(value === undefined || value === null || value === '' || (Array.isArray(value) && value.length === 0)) { @@ -290,7 +307,7 @@ Utils.getFieldLabel = (fieldName, collection) => { const label = collection.simpleSchema()._schema[fieldName].label; const nameWithSpaces = Utils.camelToSpaces(fieldName); return label || nameWithSpaces; -} +}; Utils.getLogoUrl = () => { const logoUrl = getSetting('logoUrl'); @@ -319,12 +336,12 @@ Utils.findIndex = (array, predicate) => { let continueLoop = true; array.forEach((item, currentIndex) => { if (continueLoop && predicate(item)) { - index = currentIndex - continueLoop = false + index = currentIndex; + continueLoop = false; } }); return index; -} +}; // adapted from http://stackoverflow.com/a/22072374/649299 Utils.unflatten = function(array, options, parent, level=0, tree){ @@ -382,15 +399,15 @@ Utils.stripTelescopeNamespace = (schema) => { // replace the previous schema by an object based on this filteredSchemaKeys return filteredSchemaKeys.reduce((sch, key) => ({...sch, [key]: schema[key]}), {}); -} +}; /** * Convert an array of field names into a Mongo fields specifier * @param {Array} fieldsArray */ Utils.arrayToFields = (fieldsArray) => { - return _.object(fieldsArray, _.map(fieldsArray, function () {return true})); -} + return _.object(fieldsArray, _.map(fieldsArray, function () {return true;})); +}; /** * Get the display name of a React component @@ -424,7 +441,7 @@ Utils.convertDates = (collection, listOrDocument) => { }); return Array.isArray(listOrDocument) ? convertedList : convertedList[0]; -} +}; Utils.encodeIntlError = error => typeof error !== 'object' ? error : JSON.stringify(error); @@ -454,7 +471,7 @@ Utils.decodeIntlError = (error, options = {stripped: false}) => { // check if the error has at least an 'id' expected by react-intl if (!parsedError.id) { console.error('[Undecodable error]', error); // eslint-disable-line - return {id: 'app.something_bad_happened', value: '[undecodable error]'} + return {id: 'app.something_bad_happened', value: '[undecodable error]'}; } // return the parsed error @@ -472,21 +489,21 @@ Utils.defineName = (o, name) => { return o; }; -Utils.performCheck = (operation, user, checkedObject, context, documentId) => { +Utils.performCheck = (operation, user, checkedObject, context, documentId, operationName, collectionName) => { if (!checkedObject) { - throw new Error(Utils.encodeIntlError({id: 'app.document_not_found', value: documentId})) + throwError({ id: 'app.document_not_found', data: { documentId, operationName } }); } if (!operation(user, checkedObject, context)) { - throw new Error(Utils.encodeIntlError({id: 'app.operation_not_allowed', value: operation.name})); + throwError({ id: 'app.operation_not_allowed', data: { documentId, operationName } }); } -} +}; Utils.getRoutePath = routeName => { return Routes[routeName] && Routes[routeName].path; -} +}; String.prototype.replaceAll = function(search, replacement) { var target = this; @@ -502,7 +519,7 @@ Utils.pluralize = s => { `${s}es` : `${s}s`; return plural; -} +}; Utils.removeProperty = (obj, propertyName) => { for(const prop in obj) { @@ -512,4 +529,4 @@ Utils.removeProperty = (obj, propertyName) => { Utils.removeProperty(obj[prop], propertyName); } } -} +}; diff --git a/packages/vulcan-lib/lib/modules/validation.js b/packages/vulcan-lib/lib/modules/validation.js index de069fcfb..8cf62ebf4 100644 --- a/packages/vulcan-lib/lib/modules/validation.js +++ b/packages/vulcan-lib/lib/modules/validation.js @@ -26,7 +26,7 @@ export const validateDocument = (document, collection, context) => { let validationErrors = []; // Check validity of inserted document - _.forEach(document, (value, fieldName) => { + Object.keys(document).forEach(fieldName => { const fieldSchema = schema[fieldName]; // 1. check that the current user has permission to insert each field @@ -48,7 +48,8 @@ export const validateDocument = (document, collection, context) => { // eslint-disable-next-line no-console // console.log(error); if (error.type.includes('intlError')) { - validationErrors = validationErrors.concat(JSON.parse(error.type.replace('intlError|', ''))); + const intlError = JSON.parse(error.type.replace('intlError|', '')); + validationErrors = validationErrors.concat(intlError); } else { validationErrors.push({ id: `errors.${error.type}`, @@ -125,7 +126,7 @@ export const validateModifier = (modifier, document, collection, context) => { export const validateData = (data, document, collection, context) => { return validateModifier(dataToModifier(data), document, collection, context); -} +}; /* diff --git a/packages/vulcan-lib/lib/server/accounts_helpers.js b/packages/vulcan-lib/lib/server/accounts_helpers.js index e2ab1a851..3d649f517 100644 --- a/packages/vulcan-lib/lib/server/accounts_helpers.js +++ b/packages/vulcan-lib/lib/server/accounts_helpers.js @@ -10,7 +10,7 @@ export const _tokenExpiration = (when) => { // We pass when through the Date constructor for backwards compatibility; // `when` used to be a number. return new Date((new Date(when)).getTime() + _getTokenLifetimeMs()); -} +}; // A large number of expiration days (approximately 100 years worth) that is // used when creating unexpiring tokens. @@ -25,4 +25,4 @@ export const _getTokenLifetimeMs = () => { // unexpiring token. const loginExpirationInDays = LOGIN_UNEXPIRING_TOKEN_DAYS; return (loginExpirationInDays|| DEFAULT_LOGIN_EXPIRATION_DAYS) * 24 * 60 * 60 * 1000; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/packages/vulcan-lib/lib/server/apollo_server.js b/packages/vulcan-lib/lib/server/apollo_server.js index bf2c58a7c..41265bc30 100644 --- a/packages/vulcan-lib/lib/server/apollo_server.js +++ b/packages/vulcan-lib/lib/server/apollo_server.js @@ -22,15 +22,16 @@ import { runCallbacks } from '../modules/callbacks.js'; import cookiesMiddleware from 'universal-cookie-express'; // import Cookies from 'universal-cookie'; import { _hashLoginToken, _tokenExpiration } from './accounts_helpers'; +import { getHeaderLocale } from './intl.js'; export let executableSchema; registerSetting('apolloEngine.logLevel', 'INFO', 'Log level (one of INFO, DEBUG, WARN, ERROR'); -registerSetting('apolloTracing', Meteor.isDevelopment, 'Tracing by Apollo. Default is true on development and false on prod', true); +registerSetting('apolloServer.tracing', Meteor.isDevelopment, 'Tracing by Apollo. Default is true on development and false on prod', true); // see https://github.com/apollographql/apollo-cache-control const engineApiKey = getSetting('apolloEngine.apiKey'); -const engineLogLevel = getSetting('apolloEngine.logLevel', 'INFO') +const engineLogLevel = getSetting('apolloEngine.logLevel', 'INFO'); const engineConfig = { apiKey: engineApiKey, // "origins": [ @@ -126,7 +127,7 @@ const createApolloServer = (givenOptions = {}, givenConfig = {}) => { graphQLServer.use(compression()); // GraphQL endpoint - graphQLServer.use(config.path, bodyParser.json(), graphqlExpress(async (req) => { + graphQLServer.use(config.path, bodyParser.json({ limit: getSetting('apolloServer.jsonParserOptions.limit') }), graphqlExpress(async (req) => { let options; let user = null; @@ -146,7 +147,7 @@ const createApolloServer = (givenOptions = {}, givenConfig = {}) => { } // enable tracing and caching - options.tracing = getSetting('apolloTracing', Meteor.isDevelopment); + options.tracing = getSetting('apolloServer.tracing', Meteor.isDevelopment); options.cacheControl = true; // note: custom default resolver doesn't currently work @@ -155,6 +156,10 @@ const createApolloServer = (givenOptions = {}, givenConfig = {}) => { // return source[info.fieldName]; // } + // console.log('// apollo_server.js req.renderContext'); + // console.log(req.renderContext); + // console.log('\n\n'); + // Get the token from the header if (req.headers.authorization) { const token = req.headers.authorization; @@ -194,11 +199,17 @@ const createApolloServer = (givenOptions = {}, givenConfig = {}) => { options.context[collection.options.collectionName].loader = new DataLoader(ids => findByIds(collection, ids, options.context), { cache: true }); }); - // console.log('// apollo_server.js user-agent:', req.headers['user-agent']); - // console.log('// apollo_server.js locale:', req.headers.locale); + // look for headers either in renderContext (SSR) or req (normal request to the endpoint) + const headers = req.renderContext.originalHeaders || req.headers; - options.context.locale = user && user.locale || req.headers.locale || getSetting('locale', 'en'); + options.context.locale = getHeaderLocale(headers, user && user.locale); + // console.log('// apollo_server.js isSSR?', !!req.renderContext.originalHeaders ? 'yes' : 'no'); + // console.log('// apollo_server.js headers:'); + // console.log(headers); + // console.log('// apollo_server.js final locale: ', options.context.locale); + // console.log('\n\n'); + // add error formatting from apollo-errors options.formatError = formatError; diff --git a/packages/vulcan-lib/lib/server/connectors/mongo.js b/packages/vulcan-lib/lib/server/connectors/mongo.js index b01d53161..23a2561b0 100644 --- a/packages/vulcan-lib/lib/server/connectors/mongo.js +++ b/packages/vulcan-lib/lib/server/connectors/mongo.js @@ -33,4 +33,4 @@ DatabaseConnectors.mongo = { delete: async (collection, selector, options = {}) => { return await collection.remove(convertUniqueSelector(selector)); }, -} \ No newline at end of file +}; \ No newline at end of file diff --git a/packages/vulcan-lib/lib/server/intl.js b/packages/vulcan-lib/lib/server/intl.js index 4a29c4e3b..45fce32bd 100644 --- a/packages/vulcan-lib/lib/server/intl.js +++ b/packages/vulcan-lib/lib/server/intl.js @@ -5,6 +5,7 @@ import { SchemaDirectiveVisitor } from 'apollo-server'; import { defaultFieldResolver } from 'graphql'; import { Collections } from '../modules/collections'; import { getSetting } from '../modules/settings'; +import { debug } from '../modules/debug'; import Vulcan from '../modules/config'; import { isIntlField } from '../modules/intl'; import { Connectors } from './connectors'; @@ -126,3 +127,53 @@ const migrateIntlFields = async defaultLocale => { }; Vulcan.migrateIntlFields = migrateIntlFields; + +/* + +Take a header object, and figure out the locale + +Also accepts userLocale to indicate the current user's preferred locale + +*/ +export const getHeaderLocale = (headers, userLocale) => { + + let cookieLocale, acceptedLocale, locale, localeMethod; + + // get locale from cookies + if (headers['cookie']) { + const cookies = {}; + headers['cookie'].split('; ').forEach(c => { + const cookieArray = c.split('='); + cookies[cookieArray[0]] = cookieArray[1]; + }); + cookieLocale = cookies.locale; + } + + // get locale from accepted-language header + if (headers['accept-language']) { + const acceptedLanguages = headers['accept-language'].split(',').map(l => l.split(';')[0]); + acceptedLocale = acceptedLanguages[0]; // for now only use the highest-priority accepted language + } + + if (headers.locale) { + locale = headers.locale; + localeMethod = 'header'; + } else if (cookieLocale) { + locale = cookieLocale; + localeMethod = 'cookie'; + } else if (userLocale) { + locale = userLocale; + localeMethod = 'user'; + } else if (acceptedLocale) { + locale = acceptedLocale; + localeMethod = 'browser'; + } else { + locale = getSetting('locale', 'en-US'); + localeMethod = 'setting'; + } + + debug(`// locale: ${locale} (via ${localeMethod})`); + + return locale; + +}; \ No newline at end of file diff --git a/packages/vulcan-lib/lib/server/main.js b/packages/vulcan-lib/lib/server/main.js index dd8bde8e1..e3ee9ff99 100644 --- a/packages/vulcan-lib/lib/server/main.js +++ b/packages/vulcan-lib/lib/server/main.js @@ -15,4 +15,5 @@ export * from './mutators.js'; //export * from './inject_data.js'; export * from './utils.js'; export * from './intl.js'; -export * from './accounts_helpers'; +export * from './accounts_helpers.js'; +export * from './source_version.js'; diff --git a/packages/vulcan-lib/lib/server/mutators.js b/packages/vulcan-lib/lib/server/mutators.js index f69a7a941..6a2cda8ce 100644 --- a/packages/vulcan-lib/lib/server/mutators.js +++ b/packages/vulcan-lib/lib/server/mutators.js @@ -28,10 +28,10 @@ to the client. */ import { runCallbacks, runCallbacksAsync } from '../modules/index.js'; -import { createError } from 'apollo-errors'; import { validateDocument, validateData, dataToModifier, modifierToData } from '../modules/validation.js'; import { registerSetting } from '../modules/settings.js'; import { debug, debugGroup, debugGroupEnd } from '../modules/debug.js'; +import { throwError } from '../modules/errors.js'; import { Connectors } from './connectors.js'; import pickBy from 'lodash/pickBy'; import clone from 'lodash/clone'; @@ -53,19 +53,22 @@ export const createMutator = async ({ collection, document, data, currentUser, v const schema = collection.simpleSchema()._schema; + const callbackProperties = { currentUser, collection, context }; + if (validate) { - const validationErrors = validateDocument(newDocument, collection, context); + let validationErrors = []; + + validationErrors = validationErrors.concat(validateDocument(newDocument, collection, context)); // run validation callbacks - newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.create.validate`, iterator: newDocument, properties: { currentUser, validationErrors, collection }}); - newDocument = await runCallbacks({ name: '*.create.validate', iterator: newDocument, properties: { currentUser, validationErrors, collection }}); + validationErrors = await runCallbacks({ name: `${typeName.toLowerCase()}.create.validate`, iterator: validationErrors, properties: { ...callbackProperties, document: newDocument } }); + validationErrors = await runCallbacks({ name: '*.create.validate', iterator: validationErrors, properties: { ...callbackProperties, document: newDocument } }); // OpenCRUD backwards compatibility newDocument = await runCallbacks(`${collectionName.toLowerCase()}.new.validate`, newDocument, currentUser, validationErrors); if (validationErrors.length) { - const NewDocumentValidationError = createError('app.validation_error', {message: 'app.new_document_validation_error'}); - throw new NewDocumentValidationError({data: {break: true, errors: validationErrors}}); + throwError({ id: 'app.validation_error', data: {break: true, errors: validationErrors}}); } } @@ -91,7 +94,7 @@ export const createMutator = async ({ collection, document, data, currentUser, v if (schema[fieldName].onCreate) { // OpenCRUD backwards compatibility: keep both newDocument and data for now, but phase our newDocument eventually // eslint-disable-next-line no-await-in-loop - autoValue = await schema[fieldName].onCreate({ newDocument: clone(newDocument), data: clone(newDocument), currentUser }); + autoValue = await schema[fieldName].onCreate({ newDocument: clone(newDocument), data: clone(newDocument), currentUser, context }); } else if (schema[fieldName].onInsert) { // OpenCRUD backwards compatibility // eslint-disable-next-line no-await-in-loop @@ -109,8 +112,8 @@ export const createMutator = async ({ collection, document, data, currentUser, v // } // run sync callbacks - newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.create.before`, iterator: newDocument, properties: { currentUser, collection }}); - newDocument = await runCallbacks({ name: '*.create.before', iterator: newDocument, properties: { currentUser, collection }}); + newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.create.before`, iterator: newDocument, properties: callbackProperties }); + newDocument = await runCallbacks({ name: '*.create.before', iterator: newDocument, properties: callbackProperties }); // OpenCRUD backwards compatibility newDocument = await runCallbacks(`${collectionName.toLowerCase()}.new.before`, newDocument, currentUser); newDocument = await runCallbacks(`${collectionName.toLowerCase()}.new.sync`, newDocument, currentUser); @@ -119,8 +122,8 @@ export const createMutator = async ({ collection, document, data, currentUser, v newDocument._id = await Connectors.create(collection, newDocument); // run any post-operation sync callbacks - newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.create.after`, iterator: newDocument, properties: { currentUser, collection }}); - newDocument = await runCallbacks({ name: '*.create.after', iterator: newDocument, properties: { currentUser, collection }}); + newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.create.after`, iterator: newDocument, properties: callbackProperties }); + newDocument = await runCallbacks({ name: '*.create.after', iterator: newDocument, properties: callbackProperties }); // OpenCRUD backwards compatibility newDocument = await runCallbacks(`${collectionName.toLowerCase()}.new.after`, newDocument, currentUser); @@ -130,8 +133,8 @@ export const createMutator = async ({ collection, document, data, currentUser, v // run async callbacks // note: query for document to get fresh document with collection-hooks effects applied - await runCallbacksAsync({ name: `${typeName.toLowerCase()}.create.async`, properties: { insertedDocument, currentUser, collection }}); - await runCallbacksAsync({ name: '*.create.async', properties: { insertedDocument, currentUser, collection }}); + await runCallbacksAsync({ name: `${typeName.toLowerCase()}.create.async`, properties: { insertedDocument, document: insertedDocument, ...callbackProperties }}); + await runCallbacksAsync({ name: '*.create.async', properties: { insertedDocument, document: insertedDocument, ...callbackProperties }}); // OpenCRUD backwards compatibility await runCallbacksAsync(`${collectionName.toLowerCase()}.new.async`, insertedDocument, currentUser, collection); @@ -142,7 +145,7 @@ export const createMutator = async ({ collection, document, data, currentUser, v debug(''); return { data: newDocument }; -} +}; export const updateMutator = async ({ collection, documentId, selector, data, set = {}, unset = {}, currentUser, validate, context, document }) => { @@ -166,6 +169,8 @@ export const updateMutator = async ({ collection, documentId, selector, data, se throw new Error(`Could not find document to update for selector: ${JSON.stringify(selector)}`); } + const callbackProperties = { data, document, currentUser, collection, context }; + debug(''); debugGroup(`--------------- start \x1b[36m${collectionName} Update Mutator\x1b[0m ---------------`); debug('// collectionName: ', collectionName); @@ -174,11 +179,12 @@ export const updateMutator = async ({ collection, documentId, selector, data, se if (validate) { - let validationErrors; + let validationErrors = []; - validationErrors = validateData(data, document, collection, context); - data = await runCallbacks({ name: `${typeName.toLowerCase()}.update.validate`, iterator: data, properties: { document, currentUser, validationErrors, collection }}); - data = await runCallbacks({ name: '*.update.validate', iterator: data, properties: { document, currentUser, validationErrors, collection }}); + validationErrors = validationErrors.concat(validateData(data, document, collection, context)); + + validationErrors = await runCallbacks({ name: `${typeName.toLowerCase()}.update.validate`, iterator: validationErrors, properties: callbackProperties }); + validationErrors = await runCallbacks({ name: '*.update.validate', iterator: validationErrors, properties: callbackProperties }); // OpenCRUD backwards compatibility data = modifierToData(await runCallbacks(`${collectionName.toLowerCase()}.edit.validate`, dataToModifier(data), document, currentUser, validationErrors)); @@ -187,8 +193,7 @@ export const updateMutator = async ({ collection, documentId, selector, data, se console.log('// validationErrors'); // eslint-disable-next-line no-console console.log(validationErrors); - const EditDocumentValidationError = createError('app.validation_error', { message: 'app.edit_document_validation_error' }); - throw new EditDocumentValidationError({data: {break: true, errors: validationErrors}}); + throwError({ id: 'app.validation_error', data: {break: true, errors: validationErrors}}); } } @@ -202,7 +207,7 @@ export const updateMutator = async ({ collection, documentId, selector, data, se let autoValue; if (schema[fieldName].onUpdate) { // eslint-disable-next-line no-await-in-loop - autoValue = await schema[fieldName].onUpdate({ data: clone(data), document, currentUser, newDocument }); + autoValue = await schema[fieldName].onUpdate({ data: clone(data), document, currentUser, newDocument, context }); } else if (schema[fieldName].onEdit) { // OpenCRUD backwards compatibility // eslint-disable-next-line no-await-in-loop @@ -214,8 +219,8 @@ export const updateMutator = async ({ collection, documentId, selector, data, se } // run sync callbacks - data = await runCallbacks({ name: `${typeName.toLowerCase()}.update.before`, iterator: data, properties: { document, currentUser, newDocument, collection }}); - data = await runCallbacks({ name: '*.update.before', iterator: data, properties: { document, currentUser, newDocument, collection }}); + data = await runCallbacks({ name: `${typeName.toLowerCase()}.update.before`, iterator: data, properties: { newDocument, ...callbackProperties }}); + data = await runCallbacks({ name: '*.update.before', iterator: data, properties: { newDocument, ...callbackProperties }}); // OpenCRUD backwards compatibility data = modifierToData(await runCallbacks(`${collectionName.toLowerCase()}.edit.before`, dataToModifier(data), document, currentUser, newDocument)); data = modifierToData(await runCallbacks(`${collectionName.toLowerCase()}.edit.sync`, dataToModifier(data), document, currentUser, newDocument)); @@ -247,25 +252,25 @@ export const updateMutator = async ({ collection, documentId, selector, data, se } // run any post-operation sync callbacks - newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.update.after`, iterator: newDocument, properties: { document, currentUser, collection }}); - newDocument = await runCallbacks({ name: '*.update.after', iterator: newDocument, properties: { document, currentUser, collection }}); + newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.update.after`, iterator: newDocument, properties: callbackProperties }); + newDocument = await runCallbacks({ name: '*.update.after', iterator: newDocument, properties: callbackProperties }); // OpenCRUD backwards compatibility newDocument = await runCallbacks(`${collectionName.toLowerCase()}.edit.after`, newDocument, document, currentUser); // run async callbacks - await runCallbacksAsync({ name: `${typeName.toLowerCase()}.update.async`, properties: { newDocument, document, currentUser, collection }}); - await runCallbacksAsync({ name: '*.update.async', properties: { newDocument, document, currentUser, collection }}); + await runCallbacksAsync({ name: `${typeName.toLowerCase()}.update.async`, properties: { ...callbackProperties,newDocument, document: newDocument, oldDocument: document }}); + await runCallbacksAsync({ name: '*.update.async', properties: { ...callbackProperties, newDocument, document: newDocument, oldDocument: document }}); // OpenCRUD backwards compatibility await runCallbacksAsync(`${collectionName.toLowerCase()}.edit.async`, newDocument, document, currentUser, collection); debug('\x1b[33m=> updated document with modifier: \x1b[0m'); - debug('// modifier: ', modifier) + debug('// modifier: ', modifier); debugGroupEnd(); debug(`--------------- end \x1b[36m${collectionName} Update Mutator\x1b[0m ---------------`); debug(''); return { data: newDocument }; -} +}; export const deleteMutator = async ({ collection, documentId, selector, currentUser, validate, context, document }) => { @@ -291,19 +296,32 @@ export const deleteMutator = async ({ collection, documentId, selector, currentU throw new Error(`Could not find document to delete for selector: ${JSON.stringify(selector)}`); } - // if document is not trusted, run validation callbacks + const callbackProperties = { document, currentUser, collection, context }; + if (validate) { - document = await runCallbacks({ name: `${typeName.toLowerCase()}.delete.validate`, iterator: document, properties: { currentUser, collection }}); - document = await runCallbacks({ name: '*.delete.validate', iterator: document, properties: { currentUser, collection }}); + + let validationErrors = []; + + validationErrors = await runCallbacks({ name: `${typeName.toLowerCase()}.delete.validate`, iterator: validationErrors, properties: callbackProperties }); + validationErrors = await runCallbacks({ name: '*.delete.validate', iterator: validationErrors, properties: callbackProperties }); // OpenCRUD backwards compatibility document = await runCallbacks(`${collectionName.toLowerCase()}.remove.validate`, document, currentUser); + + if (validationErrors.length) { + // eslint-disable-next-line no-console + console.log('// validationErrors'); + // eslint-disable-next-line no-console + console.log(validationErrors); + throwError({id: 'app.validation_error', data: {break: true, errors: validationErrors}}); + } + } // run onRemove step for(let fieldName of Object.keys(schema)) { if (schema[fieldName].onDelete) { // eslint-disable-next-line no-await-in-loop - await schema[fieldName].onDelete({ document, currentUser }); + await schema[fieldName].onDelete({ document, currentUser, context }); } else if (schema[fieldName].onRemove) { // OpenCRUD backwards compatibility // eslint-disable-next-line no-await-in-loop @@ -311,8 +329,8 @@ export const deleteMutator = async ({ collection, documentId, selector, currentU } } - await runCallbacks({ name: `${typeName.toLowerCase()}.delete.before`, iterator: document, properties: { currentUser, collection }}); - await runCallbacks({ name: '*.delete.before', iterator: document, properties: { currentUser, collection }}); + await runCallbacks({ name: `${typeName.toLowerCase()}.delete.before`, iterator: document, properties: callbackProperties }); + await runCallbacks({ name: '*.delete.before', iterator: document, properties: callbackProperties }); // OpenCRUD backwards compatibility await runCallbacks(`${collectionName.toLowerCase()}.remove.before`, document, currentUser); await runCallbacks(`${collectionName.toLowerCase()}.remove.sync`, document, currentUser); @@ -325,8 +343,8 @@ export const deleteMutator = async ({ collection, documentId, selector, currentU collection.loader.clear(selector.documentId); } - await runCallbacksAsync({ name: `${typeName.toLowerCase()}.delete.async`, properties: { document, currentUser, collection }}); - await runCallbacksAsync({ name: '*.delete.async', properties: { document, currentUser, collection }}); + await runCallbacksAsync({ name: `${typeName.toLowerCase()}.delete.async`, properties: callbackProperties }); + await runCallbacksAsync({ name: '*.delete.async', properties: callbackProperties }); // OpenCRUD backwards compatibility await runCallbacksAsync(`${collectionName.toLowerCase()}.remove.async`, document, currentUser, collection); @@ -335,7 +353,7 @@ export const deleteMutator = async ({ collection, documentId, selector, currentU debug(''); return { data: document }; -} +}; // OpenCRUD backwards compatibility export const newMutation = createMutator; diff --git a/packages/vulcan-lib/lib/server/query.js b/packages/vulcan-lib/lib/server/query.js index b44b4515f..695127d13 100644 --- a/packages/vulcan-lib/lib/server/query.js +++ b/packages/vulcan-lib/lib/server/query.js @@ -39,7 +39,7 @@ export const runGraphQL = async (query, variables = {}, context ) => { } return result; -} +}; export const runQuery = runGraphQL; //backwards compatibility @@ -72,7 +72,7 @@ export const buildQuery = (collection, {fragmentName, fragmentText}) => { const query = `${singleClientTemplate({ typeName, fragmentName: name })}${text}`; return query; -} +}; Meteor.startup(() => { @@ -84,7 +84,7 @@ Meteor.startup(() => { const query = buildQuery(collection, { fragmentName, fragmentText }); const result = await runQuery(query, { input: { selector: { documentId } } }, context); return result.data[Utils.camelCaseify(typeName)].result; - } + }; }); diff --git a/packages/vulcan-lib/lib/server/render_context.js b/packages/vulcan-lib/lib/server/render_context.js deleted file mode 100644 index 05e8892c8..000000000 --- a/packages/vulcan-lib/lib/server/render_context.js +++ /dev/null @@ -1,186 +0,0 @@ -// import { createMemoryHistory } from 'react-router'; -// import { compose } from 'redux'; -// import cookieParser from 'cookie-parser'; - -// import { Meteor } from 'meteor/meteor'; -// import { DDP } from 'meteor/ddp'; -// // import { Accounts } from 'meteor/accounts-base'; -// import { RoutePolicy } from 'meteor/routepolicy'; -// import { WebApp } from 'meteor/webapp'; -// import { _hashLoginToken } from './accounts_helpers'; - -// import { -// createApolloClient, -// configureStore, getActions, getReducers, getMiddlewares, -// Utils, -// } from '../modules/index.js'; - -// import { webAppConnectHandlersUse } from './meteor_patch.js'; - -// const Fiber = Npm.require('fibers'); - -// // check the req url -// function isAppUrl(req) { -// const url = req.url; -// if (url === '/favicon.ico' || url === '/robots.txt') { -// return false; -// } - -// if (url === '/app.manifest') { -// return false; -// } - -// // Avoid serving app HTML for declared routes such as /sockjs/. -// if (RoutePolicy.classify(url)) { -// return false; -// } - -// // we only need to support HTML pages only for browsers -// // Facebook's scraper uses a request header Accepts: */* -// // so allow either -// const facebookAcceptsHeader = new RegExp('/*\/*/'); -// return /html/.test(req.headers.accept) || facebookAcceptsHeader.test(req.headers.accept); -// } - -// // for meteor.user -// const LoginContext = function LoginContext(loginToken) { -// this._loginToken = loginToken; - -// // get the user -// if (Meteor.users) { -// // check to make sure, we've the loginToken, -// // otherwise a random user will fetched from the db -// let user; -// if (loginToken) { -// const hashedToken = loginToken && _hashLoginToken(loginToken); -// const query = { 'services.resume.loginTokens.hashedToken': hashedToken }; -// const options = { fields: { _id: 1 } }; -// user = Meteor.users.findOne(query, options); -// } - -// if (user) { -// this.userId = user._id; -// } -// } -// }; - -// // for req.cookies -// webAppConnectHandlersUse(cookieParser(), { order: 10, name: 'cookieParserMiddleware' }); - -// // initRenderContextMiddleware -// webAppConnectHandlersUse(Meteor.bindEnvironment(function initRenderContextMiddleware(req, res, next) { -// // check the req url -// if (!isAppUrl(req)) { -// next(); -// return; -// } - -// // init -// const history = createMemoryHistory(req.url); -// const loginToken = req.cookies && req.cookies.meteor_login_token; -// const locale = req.cookies && req.cookies.locale; -// // console.log('// render_context.js locale:', locale); -// const apolloClient = createApolloClient({ loginToken, locale }); -// let actions = {}; -// let reducers = { apollo: apolloClient.reducer() }; -// let middlewares = [Utils.defineName(apolloClient.middleware(), 'apolloClientMiddleware')]; - -// // renderContext object -// req.renderContext = { -// locale, -// history, -// loginToken, -// apolloClient, -// addAction(addedAction) { // context.addAction: add action to renderContext -// actions = { ...actions, ...addedAction }; -// return this.getActions(); -// }, -// getActions() { // SSR actions = server actions + renderContext actions -// return { ...getActions(), ...actions }; -// }, -// addReducer(addedReducer) { // context.addReducer: add reducer to renderContext -// reducers = { ...reducers, ...addedReducer }; -// return this.getReducers(); -// }, -// getReducers() { // SSR reducers = server reducers + renderContext reducers -// return { ...getReducers(), ...reducers }; -// }, -// addMiddleware(middlewareOrMiddlewareArray) { // context.addMiddleware: add middleware to renderContext -// const addedMiddleware = Array.isArray(middlewareOrMiddlewareArray) ? middlewareOrMiddlewareArray : [middlewareOrMiddlewareArray]; -// middlewares = [...middlewares, ...addedMiddleware]; -// return this.getMiddlewares(); -// }, -// getMiddlewares() { // SSR middlewares = server middlewares + renderContext middlewares -// return [...getMiddlewares(), ...middlewares]; -// }, -// }; - -// // create store -// req.renderContext.store = configureStore(req.renderContext.getReducers, {}, (store) => { -// let chain, newDispatch; - -// return next => (action) => { -// try { -// if (!chain) { -// chain = req.renderContext.getMiddlewares().map(middleware => middleware(store)); -// } -// newDispatch = compose(...chain)(next) -// return newDispatch(action); -// } catch (error) { -// // console.log(error) -// return _.identity -// } -// }; -// }) - -// // for meteor.user -// req.loginContext = new LoginContext(loginToken); - -// next(); -// }), { order: 20 }); - -// // render context object -// export const renderContext = new Meteor.EnvironmentVariable(); - -// // render context get function -// export const getRenderContext = () => renderContext.get(); - -// // withRenderContextEnvironment -// export const withRenderContextEnvironment = (fn, options = {}) => { -// // set newfn -// const newfn = (req, res, next) => { -// if (!isAppUrl(req)) { -// next(); -// return; -// } - -// Fiber.current._meteor_dynamics = Fiber.current._meteor_dynamics || []; -// Fiber.current._meteor_dynamics[DDP._CurrentInvocation.slot] = req.loginContext; -// Fiber.current._meteor_dynamics[renderContext.slot] = req.renderContext; - -// fn(req.renderContext, req, res, next); - -// if (options.autoNext) { -// next(); -// } -// }; - -// // get evfn -// const evfn = Meteor.bindEnvironment(newfn); - -// // use it -// WebApp.connectHandlers.use(evfn); - -// // get handle -// const handle = WebApp.connectHandlers.stack[WebApp.connectHandlers.stack.length - 1].handle; - -// // copy options to handle -// Object.keys(options).forEach((key) => { -// handle[key] = options[key]; -// }); -// }; - -// // withRenderContext make it easy to access context -// export const withRenderContext = (func, options = {}) => { -// withRenderContextEnvironment(func, { ...options, autoNext: true }); -// }; diff --git a/packages/vulcan-lib/lib/server/site.js b/packages/vulcan-lib/lib/server/site.js index 533325d16..b25b681a4 100644 --- a/packages/vulcan-lib/lib/server/site.js +++ b/packages/vulcan-lib/lib/server/site.js @@ -1,11 +1,13 @@ import { addGraphQLSchema, addGraphQLResolvers, addGraphQLQuery } from '../modules/graphql.js'; import { Utils } from '../modules/utils'; import { getSetting } from '../modules/settings.js'; +import { getSourceVersion } from './source_version.js'; const siteSchema = `type Site { title: String url: String logoUrl: String + sourceVersion: String }`; addGraphQLSchema(siteSchema); @@ -16,6 +18,7 @@ const siteResolvers = { title: getSetting('title'), url: getSetting('siteUrl', Meteor.absoluteUrl()), logoUrl: Utils.getLogoUrl(), + sourceVersion: getSourceVersion(), }; }, }, diff --git a/packages/vulcan-lib/lib/server/source_version.js b/packages/vulcan-lib/lib/server/source_version.js new file mode 100644 index 000000000..a7071097a --- /dev/null +++ b/packages/vulcan-lib/lib/server/source_version.js @@ -0,0 +1,23 @@ +import childProcess from 'child_process'; + +/* + +Get latest commit hash from either env variables (set with Mup for example) +or current child process. + +See https://github.com/zodern/meteor-up/issues/807#issuecomment-346915622 + +*/ +export const getSourceVersion = () => { + try { + return ( + process.env.SOURCE_VERSION || + childProcess + .execSync('git rev-parse HEAD') + .toString() + .trim() + ); + } catch (error) { + return null; + } +}; diff --git a/packages/vulcan-lib/lib/server/utils.js b/packages/vulcan-lib/lib/server/utils.js index ca6f0a13f..bccf5f1b1 100644 --- a/packages/vulcan-lib/lib/server/utils.js +++ b/packages/vulcan-lib/lib/server/utils.js @@ -1,5 +1,4 @@ import sanitizeHtml from 'sanitize-html'; - import { Utils } from '../modules'; Utils.sanitize = function(s) { diff --git a/packages/vulcan-lib/package.js b/packages/vulcan-lib/package.js index 4acd5a44f..03073d1f4 100644 --- a/packages/vulcan-lib/package.js +++ b/packages/vulcan-lib/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:lib', summary: 'Vulcan libraries.', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); diff --git a/packages/vulcan-lib/test/components.test.js b/packages/vulcan-lib/test/components.test.js new file mode 100644 index 000000000..bc1671138 --- /dev/null +++ b/packages/vulcan-lib/test/components.test.js @@ -0,0 +1,26 @@ +import { mergeWithComponents } from '../lib/modules/components'; +import { Components } from 'meteor/vulcan:core'; +import expect from 'expect'; +import { initComponentTest } from 'meteor/vulcan:test'; + +initComponentTest(); + +describe('vulcan:lib/components', function () { + describe('mergeWithComponents', function () { + const TestComponent = () => 'foo'; + const OverrideTestComponent = () => 'bar'; + Components.TestComponent = TestComponent; + it('override existing components', function () { + const MyComponents = { TestComponent: OverrideTestComponent }; + const MergedComponents = mergeWithComponents(MyComponents); + expect(MergedComponents.TestComponent).toEqual(OverrideTestComponent); + // eslint-disable-next-line + expect(MergedComponents.TestComponent()).toEqual('bar'); + }); + it('return \'Components\' if no components are provided', function () { + expect(mergeWithComponents()).toEqual(Components); + expect(mergeWithComponents().TestComponent).toEqual(TestComponent); + }); + + }); +}); diff --git a/packages/vulcan-lib/test/handleOptions.test.js b/packages/vulcan-lib/test/handleOptions.test.js new file mode 100644 index 000000000..b26d8d22a --- /dev/null +++ b/packages/vulcan-lib/test/handleOptions.test.js @@ -0,0 +1,44 @@ +import { extractCollectionInfo, extractFragmentInfo } from '../lib/modules/handleOptions'; +import expect from 'expect'; + +describe('vulcan:lib/handleOptions', function() { + const expectedCollectionName = 'COLLECTION_NAME'; + const expectedCollection = { options: { collectionName: expectedCollectionName } }; + + it('get collectionName from collection', function() { + const options = { collection: expectedCollection }; + const { collection, collectionName } = extractCollectionInfo(options); + expect(collection).toEqual(expectedCollection); + expect(collectionName).toEqual(expectedCollectionName); + }); + it.skip('get collection from collectionName', function() { + // TODO: mock getCollection + const options = { collectionName: expectedCollectionName }; + const { collection, collectionName } = extractCollectionInfo(options); + expect(collection).toEqual(expectedCollection); + expect(collectionName).toEqual(expectedCollectionName); + }); + const expectedFragmentName = 'FRAGMENT_NAME'; + const expectedFragment = { definitions: [{ name: { value: expectedFragmentName } }] }; + it.skip('get fragment from fragmentName', function() { + // TODO: mock getCollection + const options = { fragmentName: expectedFragmentName }; + const { fragment, fragmentName } = extractFragmentInfo(options); + expect(fragment).toEqual(expectedFragment); + expect(fragmentName).toEqual(expectedFragmentName); + }); + it('get fragmentName from fragment', function() { + const options = { fragment: expectedFragment }; + const { fragment, fragmentName } = extractFragmentInfo(options); + expect(fragment).toEqual(expectedFragment); + expect(fragmentName).toEqual(expectedFragmentName); + }); + it.skip('get fragmentName and fragment from collectionName', function() { + // TODO: mock getFragment + // if options does not contain fragment, we get the collection default fragment based on its name + const options = {}; + const { fragment, fragmentName } = extractFragmentInfo(options, expectedCollectionName); + expect(fragment).toEqual(expectedFragment); + expect(fragmentName).toEqual(expectedFragmentName); + }); +}); diff --git a/packages/vulcan-lib/test/index.js b/packages/vulcan-lib/test/index.js index 1539625cf..bfa600ea7 100644 --- a/packages/vulcan-lib/test/index.js +++ b/packages/vulcan-lib/test/index.js @@ -1 +1,3 @@ -import './client/index'; \ No newline at end of file +import './client/index'; +import './handleOptions.test.js'; +import './components.test.js'; diff --git a/packages/vulcan-newsletter/lib/components/NewsletterSubscribe.jsx b/packages/vulcan-newsletter/lib/components/NewsletterSubscribe.jsx index 80a9ccf06..994a90e5e 100644 --- a/packages/vulcan-newsletter/lib/components/NewsletterSubscribe.jsx +++ b/packages/vulcan-newsletter/lib/components/NewsletterSubscribe.jsx @@ -48,7 +48,7 @@ class NewsletterSubscribe extends PureComponent {
- ) + ); } } diff --git a/packages/vulcan-newsletter/lib/modules/collection.js b/packages/vulcan-newsletter/lib/modules/collection.js index 4108258a6..c16535e9f 100644 --- a/packages/vulcan-newsletter/lib/modules/collection.js +++ b/packages/vulcan-newsletter/lib/modules/collection.js @@ -30,7 +30,7 @@ const schema = { type: String, optional: true, }, -} +}; Newsletters.attachSchema(new SimpleSchema(schema)); diff --git a/packages/vulcan-newsletter/lib/server/integrations/mailchimp.js b/packages/vulcan-newsletter/lib/server/integrations/mailchimp.js index 50a5b42c3..ed82f30eb 100644 --- a/packages/vulcan-newsletter/lib/server/integrations/mailchimp.js +++ b/packages/vulcan-newsletter/lib/server/integrations/mailchimp.js @@ -3,10 +3,9 @@ // newsletter scheduling with MailChimp import moment from 'moment'; -import { getSetting, registerSetting } from 'meteor/vulcan:core'; +import { getSetting, registerSetting, throwError } from 'meteor/vulcan:core'; import Newsletters from '../../modules/collection.js'; import MailChimpNPM from 'mailchimp'; -import { createError } from 'apollo-errors'; registerSetting('mailchimp', null, 'MailChimp settings'); @@ -49,7 +48,7 @@ if (settings) { const subscribe = callSyncAPI('lists', 'subscribe', subscribeOptions); return {result: 'subscribed', ...subscribe}; } catch (error) { - console.log(error) + console.log(error); let name; const message = error.message; if (error.code == 214) { @@ -59,8 +58,7 @@ if (settings) { } else { name = 'subscription_failed'; } - const NewsletterError = createError(name, { message }); - throw new NewsletterError({ data: {path: 'newsletter_subscribeToNewsletter', message}}); + throwError({ id: name, message, data: {path: 'newsletter_subscribeToNewsletter', message}}); } }, @@ -126,6 +124,6 @@ if (settings) { } } - } + }; } diff --git a/packages/vulcan-newsletter/lib/server/integrations/sample.js b/packages/vulcan-newsletter/lib/server/integrations/sample.js index 17e35b2bd..f6a5636a6 100644 --- a/packages/vulcan-newsletter/lib/server/integrations/sample.js +++ b/packages/vulcan-newsletter/lib/server/integrations/sample.js @@ -78,6 +78,6 @@ if (settings) { return sendSync(options); } - } + }; } \ No newline at end of file diff --git a/packages/vulcan-newsletter/lib/server/integrations/sendy.js b/packages/vulcan-newsletter/lib/server/integrations/sendy.js index f8bdd166f..a14a3537c 100644 --- a/packages/vulcan-newsletter/lib/server/integrations/sendy.js +++ b/packages/vulcan-newsletter/lib/server/integrations/sendy.js @@ -27,7 +27,7 @@ if (settings) { // eslint-disable-next-line no-console console.log(error); if (error.message === 'Already subscribed.') { - return {result: 'already-subscribed'} + return {result: 'already-subscribed'}; } } }; @@ -87,6 +87,6 @@ if (settings) { return createCampaignSync(params); } - } + }; } \ No newline at end of file diff --git a/packages/vulcan-newsletter/lib/server/main.js b/packages/vulcan-newsletter/lib/server/main.js index c3823f0bd..2cc2d2142 100644 --- a/packages/vulcan-newsletter/lib/server/main.js +++ b/packages/vulcan-newsletter/lib/server/main.js @@ -5,7 +5,7 @@ import './cron.js'; import './mutations.js'; import './callbacks.js'; -import './integrations/sendy.js'; +// import './integrations/sendy.js'; import './integrations/mailchimp.js'; export default Newsletters; \ No newline at end of file diff --git a/packages/vulcan-newsletter/lib/server/mutations.js b/packages/vulcan-newsletter/lib/server/mutations.js index f83cec6f8..f3b42b65b 100644 --- a/packages/vulcan-newsletter/lib/server/mutations.js +++ b/packages/vulcan-newsletter/lib/server/mutations.js @@ -43,7 +43,7 @@ const resolver = { try { return await Newsletters.unsubscribeUser(user); } catch (error) { - const errorMessage = error.message.includes('subscription-failed') ? Utils.encodeIntlError({id: 'newsletter.subscription_failed'}) : error.message + const errorMessage = error.message.includes('subscription-failed') ? Utils.encodeIntlError({id: 'newsletter.subscription_failed'}) : error.message; throw new Error(errorMessage); } }, diff --git a/packages/vulcan-newsletter/lib/server/newsletters.js b/packages/vulcan-newsletter/lib/server/newsletters.js index 1e59b9351..0bcef5748 100644 --- a/packages/vulcan-newsletter/lib/server/newsletters.js +++ b/packages/vulcan-newsletter/lib/server/newsletters.js @@ -46,9 +46,9 @@ Newsletters.subscribeUser = async (user, confirm = false) => { console.log(`// Adding ${email} to ${provider} list…`); const result = Newsletters[provider].subscribe(email, confirm); // eslint-disable-next-line no-console - if (result) {console.log ('-> added')} + if (result) {console.log ('-> added');} await Connectors.update(Users, user._id, {$set: {newsletter_subscribeToNewsletter: true}}); -} +}; /** * @summary Subscribe an email to the newsletter @@ -59,8 +59,8 @@ Newsletters.subscribeEmail = (email, confirm = false) => { console.log(`// Adding ${email} to ${provider} list…`); const result = Newsletters[provider].subscribe(email, confirm); // eslint-disable-next-line no-console - if (result) {console.log ('-> added')} -} + if (result) {console.log ('-> added');} +}; /** @@ -77,7 +77,7 @@ Newsletters.unsubscribeUser = async (user) => { console.log('// Removing "'+email+'" from list…'); Newsletters[provider].unsubscribe(email); await Connectors.update(Users, user._id, {$set: {newsletter_subscribeToNewsletter: false}}); -} +}; /** * @summary Unsubscribe an email from the newsletter @@ -87,7 +87,7 @@ Newsletters.unsubscribeEmail = (email) => { // eslint-disable-next-line no-console console.log('// Removing "'+email+'" from list…'); Newsletters[provider].unsubscribe(email); -} +}; /** * @summary Build a newsletter subject from an array of posts @@ -97,7 +97,7 @@ Newsletters.unsubscribeEmail = (email) => { Newsletters.getSubject = posts => { const subject = posts.map((post, index) => index > 0 ? `, ${post.title}` : post.title).join(''); return Utils.trimWords(subject, 15); -} +}; /** * @summary Build a newsletter campaign from an array of posts @@ -222,14 +222,14 @@ Newsletters.getSubject = posts => { Newsletters.getNext = () => { var nextJob = SyncedCron.nextScheduledAtDate('scheduleNewsletter'); return nextJob; -} +}; /** * @summary Get the last sent newsletter */ Newsletters.getLast = () => { return Newsletters.findOne({}, {sort: {createdAt: -1}}); -} +}; /** * @summary Send the newsletter @@ -283,7 +283,7 @@ Newsletters.send = async (isTest = false) => { console.log('No newsletter to schedule today…'); } -} +}; Meteor.startup(() => { if(!Newsletters[provider]) { diff --git a/packages/vulcan-newsletter/package.js b/packages/vulcan-newsletter/package.js index 42db3ed93..e7924865a 100644 --- a/packages/vulcan-newsletter/package.js +++ b/packages/vulcan-newsletter/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:newsletter', summary: 'Vulcan email newsletter package', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -10,8 +10,8 @@ Package.onUse(function (api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:core@1.12.8', - 'vulcan:email@1.12.8' + 'vulcan:core@1.12.13', + 'vulcan:email@1.12.13' ]); api.mainModule('lib/server/main.js', 'server'); diff --git a/packages/vulcan-payments/README.md b/packages/vulcan-payments/README.md index ed47d517a..cb5e9df47 100644 --- a/packages/vulcan-payments/README.md +++ b/packages/vulcan-payments/README.md @@ -92,7 +92,7 @@ Make sure you define your products in a location accessible to both client and s associatedCollection={Jobs} associatedId={job._id} callback={setToPaid} - button={} + button={} /> ``` diff --git a/packages/vulcan-payments/lib/components/ChargesDashboard.jsx b/packages/vulcan-payments/lib/components/ChargesDashboard.jsx index 21459ab1e..39f13909d 100644 --- a/packages/vulcan-payments/lib/components/ChargesDashboard.jsx +++ b/packages/vulcan-payments/lib/components/ChargesDashboard.jsx @@ -6,8 +6,8 @@ import { registerComponent, Components } from 'meteor/vulcan:lib'; // {document._id} // } -const StripeId = ({ document }) => - {document.stripeId} +const StripeId = ({ document }) => + {document.stripeId}; const ChargesDashboard = props =>
@@ -36,6 +36,6 @@ const ChargesDashboard = props => }, ]} /> -
+
; registerComponent('ChargesDashboard', ChargesDashboard); \ No newline at end of file diff --git a/packages/vulcan-payments/lib/components/Checkout.jsx b/packages/vulcan-payments/lib/components/Checkout.jsx index c6ce831f5..ee46582f2 100644 --- a/packages/vulcan-payments/lib/components/Checkout.jsx +++ b/packages/vulcan-payments/lib/components/Checkout.jsx @@ -20,6 +20,12 @@ class Checkout extends React.Component { }; } + handleOpen = () => { + if (this.props.onClick) { + this.props.onClick(); + } + } + onToken(token) { const {paymentActionMutation, productKey, associatedCollection, associatedDocument, callback, successCallback, errorCallback, properties, currentUser, flash, coupon} = this.props; @@ -34,7 +40,7 @@ class Checkout extends React.Component { associatedId: associatedDocument._id, properties, coupon, - } + }; paymentActionMutation(args).then(response => { @@ -71,7 +77,7 @@ class Checkout extends React.Component { name: 'My Cool Product', description: 'This product is awesome.', currency: 'USD', - } + }; // get the product from Products (either object or function applied to doc) // or default to sample product @@ -88,6 +94,7 @@ class Checkout extends React.Component { return (
{this.state.loading ? : null}
- ) + ); } } @@ -117,13 +124,14 @@ Checkout.propTypes = { coupon: PropTypes.string, associatedDocument: PropTypes.object, customAmount: PropTypes.number, + onClick: PropTypes.func, }; const WrappedCheckout = (props) => { const { fragment, fragmentName } = props; const WrappedCheckout = withPaymentAction({fragment, fragmentName})(Checkout); return ; -} +}; registerComponent('Checkout', WrappedCheckout, withCurrentUser, withMessages); diff --git a/packages/vulcan-payments/lib/modules/charges/collection.js b/packages/vulcan-payments/lib/modules/charges/collection.js index 9e3a347f8..1037752c0 100644 --- a/packages/vulcan-payments/lib/modules/charges/collection.js +++ b/packages/vulcan-payments/lib/modules/charges/collection.js @@ -25,6 +25,6 @@ Charges.addDefaultView(terms => { Charges.checkAccess = (currentUser, charge) => { return Users.isAdmin(currentUser); -} +}; export default Charges; diff --git a/packages/vulcan-payments/lib/modules/products.js b/packages/vulcan-payments/lib/modules/products.js index 3983cf115..4e925eaef 100644 --- a/packages/vulcan-payments/lib/modules/products.js +++ b/packages/vulcan-payments/lib/modules/products.js @@ -11,7 +11,7 @@ export const addProduct = (productKey, product, productType = 'product') => { const returnValue = product(document); returnValue.type = productType; return returnValue; - } + }; } else { productWithType = product; productWithType.type = productType; diff --git a/packages/vulcan-payments/lib/server/integrations/stripe.js b/packages/vulcan-payments/lib/server/integrations/stripe.js index 24905238e..65e124ef1 100644 --- a/packages/vulcan-payments/lib/server/integrations/stripe.js +++ b/packages/vulcan-payments/lib/server/integrations/stripe.js @@ -102,7 +102,7 @@ export const receiveAction = async (args) => { userProfile: Users.getProfileUrl(user, true), productKey, ...properties - } + }; if (associatedCollection && associatedId) { metadata.associatedCollection = associatedCollection; @@ -121,15 +121,17 @@ export const receiveAction = async (args) => { runCallbacks('stripe.receive.async', { metadata, user, product, collection, document, args }); return returnDocument; -} +}; /* Retrieve or create a Stripe customer */ -export const getCustomer = async (user, id) => { +export const getCustomer = async (user, token) => { + const { id } = token; + let customer; try { @@ -156,7 +158,7 @@ export const getCustomer = async (user, id) => { } return customer; -} +}; /* @@ -194,7 +196,7 @@ export const createCharge = async ({user, product, collection, document, metadat runCallbacksAsync('stripe.charge.async', { charge, collection, document, args, user }); - return processAction({collection, document, stripeObject: charge, args, user}) + return processAction({collection, document, stripeObject: charge, args, user}); }; @@ -260,7 +262,7 @@ export const createSubscription = async ({user, product, collection, document, m runCallbacksAsync('stripe.subscribe.async', {subscription, collection, returnDocument, args, user}); - returnDocument = await processAction({collection, document, stripeObject: subscription, args, user}) + returnDocument = await processAction({collection, document, stripeObject: subscription, args, user}); return returnDocument; @@ -408,7 +410,7 @@ export const processAction = async ({collection, document, stripeObject, args, u debug(''); return returnDocument; -} +}; /* @@ -493,7 +495,7 @@ app.post('/stripe', async function(req, res) { // make sure document actually exists if (!document) { - throw new Error(`Could not find ${associatedCollection} document with id ${associatedId} associated with subscription id ${subscription.id}; Not processing charge.`) + throw new Error(`Could not find ${associatedCollection} document with id ${associatedId} associated with subscription id ${subscription.id}; Not processing charge.`); } const args = { @@ -502,7 +504,7 @@ app.post('/stripe', async function(req, res) { associatedCollection, associatedId, livemode: subscription.livemode, - } + }; processAction({ collection, document, stripeObject: charge, args}); diff --git a/packages/vulcan-payments/lib/server/mutations.js b/packages/vulcan-payments/lib/server/mutations.js index 53ce852b4..481379444 100644 --- a/packages/vulcan-payments/lib/server/mutations.js +++ b/packages/vulcan-payments/lib/server/mutations.js @@ -15,7 +15,7 @@ addGraphQLMutation('paymentActionMutation(token: JSON, userId: String, productKe function CreateChargeableUnionType() { const chargeableSchema = `union Chargeable = ${Collections.map(collection => collection.typeName).join(' | ')}`; addGraphQLSchema(chargeableSchema); - return {} + return {}; } addCallback('graphql.init.before', CreateChargeableUnionType); diff --git a/packages/vulcan-payments/package.js b/packages/vulcan-payments/package.js index 5a64bfe55..0d4eddee4 100644 --- a/packages/vulcan-payments/package.js +++ b/packages/vulcan-payments/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:payments', summary: 'Vulcan payments package', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -11,7 +11,7 @@ Package.onUse(function (api) { api.use([ 'promise', - 'vulcan:core@1.12.8', + 'vulcan:core@1.12.13', 'fourseven:scss@4.5.4', ]); diff --git a/packages/vulcan-routing/lib/client/routing.jsx b/packages/vulcan-routing/lib/client/routing.jsx index 769cfda42..0275f2b76 100644 --- a/packages/vulcan-routing/lib/client/routing.jsx +++ b/packages/vulcan-routing/lib/client/routing.jsx @@ -44,14 +44,14 @@ Meteor.startup(() => { context.initialState = initialState; const apolloClientReducer = (state = {}, action) => { if (initialState && initialState.apollo && !_.isEmpty(initialState.apollo.data) && _.isEmpty(state.data)) { - state = initialState.apollo + state = initialState.apollo; } const newState = context.apolloClient.reducer()(state, action); return newState; - } + }; context.addReducer({ apollo: apolloClientReducer }); context.store.reload(); - context.store.dispatch({ type: '@@nova/INIT' }) // the first dispatch will generate a newDispatch function from middleware + context.store.dispatch({ type: '@@nova/INIT' }); // the first dispatch will generate a newDispatch function from middleware runCallbacks('router.client.rehydrate', { initialState, store: context.store}); }, historyHook(newHistory) { diff --git a/packages/vulcan-routing/lib/server/router.jsx b/packages/vulcan-routing/lib/server/router.jsx index eded83e76..181ce9d67 100644 --- a/packages/vulcan-routing/lib/server/router.jsx +++ b/packages/vulcan-routing/lib/server/router.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { match, RouterContext, createMemoryHistory } from 'react-router'; -import { renderToString } from 'react-dom/server' -import { ServerStyleSheet } from 'styled-components' +import { renderToString } from 'react-dom/server'; +import { ServerStyleSheet } from 'styled-components'; import moment from 'moment'; import { RoutePolicy } from 'meteor/routepolicy'; @@ -75,9 +75,8 @@ function generateSSRData(options, req, res, renderProps) { css = req.css; } catch (err) { - console.log('url: ', req.url); // eslint-disable-line no-console - console.log(err); // eslint-disable-line no-console - console.error(new Date(), 'error while server-rendering', err.stack); // eslint-disable-line no-console + console.error(`Error while server-rendering. date: ${new Date().toString()} url: ${req.url}`); // eslint-disable-line no-console + console.error(err); // eslint-disable-line no-console } return { html, css, styledComponentCss }; diff --git a/packages/vulcan-routing/lib/server/routing.jsx b/packages/vulcan-routing/lib/server/routing.jsx index de9668651..0755e863c 100644 --- a/packages/vulcan-routing/lib/server/routing.jsx +++ b/packages/vulcan-routing/lib/server/routing.jsx @@ -2,7 +2,6 @@ import React from 'react'; import Helmet from 'react-helmet'; import { getDataFromTree, ApolloProvider } from 'react-apollo'; import { CookiesProvider } from 'react-cookie'; - import { Meteor } from 'meteor/meteor'; import { @@ -11,10 +10,16 @@ import { Routes, populateComponentsApp, populateRoutesApp, initializeFragments, getRenderContext, runCallbacks, + getHeaderLocale, } from 'meteor/vulcan:lib'; import { RouterServer } from './router.jsx'; +//fix helmet when running tests +if (Meteor.isPackageTest) { + Helmet.canUseDOM = false; +} + Meteor.startup(() => { // note: route defined here because it "shouldn't be removable" addRoute({name:'app.notfound', path:'*', componentName: 'Error404'}); @@ -49,9 +54,13 @@ Meteor.startup(() => { wrapperHook(req, res, appGenerator) { const { apolloClient, store } = getRenderContext(); store.reload(); - store.dispatch({ type: '@@nova/INIT' }) // the first dispatch will generate a newDispatch function from middleware + store.dispatch({ type: '@@nova/INIT' }); // the first dispatch will generate a newDispatch function from middleware const app = runCallbacks('router.server.wrapper', appGenerator(), { req, res, store, apolloClient }); - return {app}; + const locale = getHeaderLocale(req.headers ); + const appWithLocale = React.cloneElement(app, { locale }); + // TODO: currently locale is passed through cookies as a hack because it's not available as props; fix this + req.universalCookies.cookies.locale = locale; + return {appWithLocale}; }, preRender(req, res, app) { runCallbacks('router.server.preRender', { req, res, app }); diff --git a/packages/vulcan-routing/package.js b/packages/vulcan-routing/package.js index d897d9b21..5c1245435 100644 --- a/packages/vulcan-routing/package.js +++ b/packages/vulcan-routing/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:routing', summary: 'Vulcan router package', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -10,7 +10,7 @@ Package.onUse(function (api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:lib@1.12.8', + 'vulcan:lib@1.12.13', ]); api.mainModule('lib/server/main.js', 'server'); diff --git a/packages/vulcan-subscribe/lib/components/SubscribeTo.jsx b/packages/vulcan-subscribe/lib/components/SubscribeTo.jsx index d90f12a96..fed332d93 100644 --- a/packages/vulcan-subscribe/lib/components/SubscribeTo.jsx +++ b/packages/vulcan-subscribe/lib/components/SubscribeTo.jsx @@ -7,7 +7,7 @@ import Users from 'meteor/vulcan:users'; import { withCurrentUser, withMessages, registerComponent, Utils } from 'meteor/vulcan:core'; // boolean -> unsubscribe || subscribe -const getSubscribeAction = subscribed => subscribed ? 'unsubscribe' : 'subscribe' +const getSubscribeAction = subscribed => subscribed ? 'unsubscribe' : 'subscribe'; class SubscribeToActionHandler extends PureComponent { diff --git a/packages/vulcan-subscribe/lib/mutations.js b/packages/vulcan-subscribe/lib/mutations.js index f0a5b2a97..2a5721a27 100644 --- a/packages/vulcan-subscribe/lib/mutations.js +++ b/packages/vulcan-subscribe/lib/mutations.js @@ -83,7 +83,7 @@ const performSubscriptionAction = (action, collection, itemId, user) => { // - the action is subscribe but the user has already subscribed to this item // - the action is unsubscribe but the user hasn't subscribed to this item if (!subscription || (action === 'subscribe' && subscription.hasSubscribedItem) || (action === 'unsubscribe' && !subscription.hasSubscribedItem)) { - throw Error(Utils.encodeIntlError({id: 'app.mutation_not_allowed', value: 'Already subscribed'})) + throw Error(Utils.encodeIntlError({id: 'app.mutation_not_allowed', value: 'Already subscribed'})); } // shorthand for useful variables @@ -127,7 +127,7 @@ const performSubscriptionAction = (action, collection, itemId, user) => { return updatedUser; } else { - throw Error(Utils.encodeIntlError({id: 'app.something_bad_happened'})) + throw Error(Utils.encodeIntlError({id: 'app.something_bad_happened'})); } }; diff --git a/packages/vulcan-subscribe/package.js b/packages/vulcan-subscribe/package.js index 18c2847d0..21bea6467 100644 --- a/packages/vulcan-subscribe/package.js +++ b/packages/vulcan-subscribe/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:subscribe', summary: 'Subscribe to posts, users, etc. to be notified of new activity', - version: '1.12.8', + version: '1.12.13', git: 'https://github.com/VulcanJS/Vulcan.git' }); @@ -11,14 +11,14 @@ Package.onUse(function (api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:core@1.12.8', + 'vulcan:core@1.12.13', // dependencies on posts, categories are done with nested imports to reduce explicit dependencies ]); api.use([ - 'vulcan:posts@1.12.8', - 'vulcan:comments@1.12.8', - 'vulcan:categories@1.12.8', + 'vulcan:posts@1.12.13', + 'vulcan:comments@1.12.13', + 'vulcan:categories@1.12.13', ], {weak: true}); api.mainModule('lib/modules.js', ['client']); diff --git a/packages/vulcan-test/lib/client/main.js b/packages/vulcan-test/lib/client/main.js new file mode 100644 index 000000000..cbbfc4b78 --- /dev/null +++ b/packages/vulcan-test/lib/client/main.js @@ -0,0 +1 @@ +export * from '../modules'; \ No newline at end of file diff --git a/packages/vulcan-test/lib/modules/index.js b/packages/vulcan-test/lib/modules/index.js new file mode 100644 index 000000000..b5b0d883c --- /dev/null +++ b/packages/vulcan-test/lib/modules/index.js @@ -0,0 +1 @@ +export { default as initComponentTest } from './initComponentTest'; \ No newline at end of file diff --git a/packages/vulcan-test/lib/modules/initComponentTest.js b/packages/vulcan-test/lib/modules/initComponentTest.js new file mode 100644 index 000000000..65e76c0f5 --- /dev/null +++ b/packages/vulcan-test/lib/modules/initComponentTest.js @@ -0,0 +1,21 @@ +/** + * Initialize components + * Must be called AFTER components registration + */ +// setup JSDOM server side for testing (necessary for Enzyme to mount) +import 'jsdom-global/register'; +import Enzyme from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16.3'; + +const initComponentTest = () => { + // setup enzyme + Enzyme.configure({ adapter: new Adapter() }); + // and then load them in the app so that is defined + import { populateComponentsApp, initializeFragments } from 'meteor/vulcan:lib'; + // we need registered fragments to be initialized because populateComponentsApp will run + // hocs, like withUpdate, that rely on fragments + initializeFragments(); + // actually fills the Components object + populateComponentsApp(); +}; +export default initComponentTest; \ No newline at end of file diff --git a/packages/vulcan-test/lib/server/main.js b/packages/vulcan-test/lib/server/main.js new file mode 100644 index 000000000..cbbfc4b78 --- /dev/null +++ b/packages/vulcan-test/lib/server/main.js @@ -0,0 +1 @@ +export * from '../modules'; \ No newline at end of file diff --git a/packages/vulcan-test/package.js b/packages/vulcan-test/package.js new file mode 100644 index 000000000..96ef49f06 --- /dev/null +++ b/packages/vulcan-test/package.js @@ -0,0 +1,19 @@ +Package.describe({ + name: 'vulcan:test', + summary: 'Vulcan test helpers', + version: '1.12.8', + git: 'https://github.com/VulcanJS/Vulcan.git' +}); + +Package.onUse(function (api) { + + api.versionsFrom('1.6.1'); + + api.use([ + 'vulcan:lib@1.12.8', + ]); + + api.mainModule('lib/server/main.js', 'server'); + api.mainModule('lib/client/main.js', 'client'); + +}); diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/Date2.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/Date2.jsx index 24cbcdb9b..e2c1af68a 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/Date2.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/Date2.jsx @@ -49,7 +49,7 @@ class DateComponent2 extends PureComponent { onChange: (name, value) => { this.updateDate({ month: value }); } - } + }; const dayProperties = { label: 'Day', @@ -60,7 +60,7 @@ class DateComponent2 extends PureComponent { onBlur: (e) => { this.updateDate({ day: e.target.value }); } - } + }; const yearProperties = { label: 'Year', @@ -71,7 +71,7 @@ class DateComponent2 extends PureComponent { onBlur: (e) => { this.updateDate({ year: e.target.value }); } - } + }; return (
diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/FormComponentInner.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/FormComponentInner.jsx index b77efc096..4447ef31e 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/FormComponentInner.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/FormComponentInner.jsx @@ -52,8 +52,11 @@ class FormComponentInner extends PureComponent { showCharsRemaining, charsRemaining, renderComponent, + formComponents, } = this.props; + const FormComponents = formComponents; + const hasErrors = errors && errors.length; const inputName = typeof input === 'function' ? input.name : input; @@ -72,7 +75,7 @@ class FormComponentInner extends PureComponent {
{instantiateComponent(beforeComponent, properties)} - {hasErrors ? : null} + {hasErrors ? : null} {this.renderClear()} {showCharsRemaining && (
{charsRemaining}
diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/Radiogroup.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/Radiogroup.jsx index 803f7dc60..9d2c88e1a 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/Radiogroup.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/Radiogroup.jsx @@ -2,6 +2,6 @@ import React from 'react'; import { RadioGroup } from 'formsy-react-components'; import { registerComponent } from 'meteor/vulcan:core'; -const RadioGroupComponent = ({refFunction, inputProperties, ...properties}) => +const RadioGroupComponent = ({refFunction, inputProperties, ...properties}) => ; registerComponent('FormComponentRadioGroup', RadioGroupComponent); \ No newline at end of file diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/Select.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/Select.jsx index 600ed344d..0c7c7be0b 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/Select.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/Select.jsx @@ -14,7 +14,7 @@ const SelectComponent = ({refFunction, inputProperties, datatype, ...properties} }; let otherOptions = Array.isArray(inputProperties.options) && inputProperties.options.length ? inputProperties.options : []; const options = [noneOption, ...otherOptions]; - return ; }; SelectComponent.contextTypes = { diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/SelectMultiple.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/SelectMultiple.jsx index 743add582..7d2250ca3 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/SelectMultiple.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/SelectMultiple.jsx @@ -6,7 +6,7 @@ import { registerComponent } from 'meteor/vulcan:core'; const SelectMultipleComponent = ({refFunction, inputProperties, ...properties}, { intl }) => { inputProperties.multiple = true; - return ; }; SelectMultipleComponent.contextTypes = { diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/StaticText.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/StaticText.jsx index c4f2a7418..2edfc377e 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/StaticText.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/StaticText.jsx @@ -2,8 +2,8 @@ import React from 'react'; import { registerComponent } from 'meteor/vulcan:core'; const parseUrl = value => { - return value.slice(0,4) === 'http' ? {value} : value; -} + return value && value.toString().slice(0,4) === 'http' ? {value} : value; +}; const StaticComponent = ({ value, label }) => (
diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/Textarea.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/Textarea.jsx index 29db1d23e..33272d70f 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/Textarea.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/Textarea.jsx @@ -2,6 +2,6 @@ import React from 'react'; import { Textarea } from 'formsy-react-components'; import { registerComponent } from 'meteor/vulcan:core'; -const TextareaComponent = ({refFunction, inputProperties, ...properties}) =>