diff --git a/docs/providers/aws/README.md b/docs/providers/aws/README.md
index f25b4437e..d464d02fc 100644
--- a/docs/providers/aws/README.md
+++ b/docs/providers/aws/README.md
@@ -62,6 +62,7 @@ If you have questions, join the [chat in gitter](https://gitter.im/serverless/se
Invoke
Invoke Local
Logs
+ Login
Metrics
Info
Rollback
diff --git a/docs/providers/aws/cli-reference/login.md b/docs/providers/aws/cli-reference/login.md
new file mode 100644
index 000000000..81f42e851
--- /dev/null
+++ b/docs/providers/aws/cli-reference/login.md
@@ -0,0 +1,24 @@
+
+
+
+### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/cli-reference/login)
+
+
+# Login
+
+The `login` command logs users into the serverless platform.
+
+It will create a new serverless platform account if one doesn't already exist.
+
+```bash
+serverless login
+
+# Shorthand
+sls login
+```
diff --git a/docs/providers/azure/README.md b/docs/providers/azure/README.md
index 3466edb5a..6991b338d 100644
--- a/docs/providers/azure/README.md
+++ b/docs/providers/azure/README.md
@@ -54,6 +54,7 @@ If you have questions, join the [chat in gitter](https://gitter.im/serverless/se
Deploy
Deploy Function
Invoke
+ Login
Logs
Remove
diff --git a/docs/providers/azure/cli-reference/login.md b/docs/providers/azure/cli-reference/login.md
new file mode 100644
index 000000000..d1fac3116
--- /dev/null
+++ b/docs/providers/azure/cli-reference/login.md
@@ -0,0 +1,24 @@
+
+
+
+### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/azure/cli-reference/login)
+
+
+# Login
+
+The `login` command logs users into the serverless platform.
+
+It will create a new serverless platform account if one doesn't already exist.
+
+```bash
+serverless login
+
+# Shorthand
+sls login
+```
diff --git a/docs/providers/google/README.md b/docs/providers/google/README.md
index 107047353..cfaf54714 100644
--- a/docs/providers/google/README.md
+++ b/docs/providers/google/README.md
@@ -54,6 +54,7 @@ If you have questions, join the [chat in gitter](https://gitter.im/serverless/se
Deploy
Info
Invoke
+ Login
Logs
Remove
diff --git a/docs/providers/google/cli-reference/login.md b/docs/providers/google/cli-reference/login.md
new file mode 100644
index 000000000..6ff65da15
--- /dev/null
+++ b/docs/providers/google/cli-reference/login.md
@@ -0,0 +1,24 @@
+
+
+
+### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/google/cli-reference/login)
+
+
+# Login
+
+The `login` command logs users into the serverless platform.
+
+It will create a new serverless platform account if one doesn't already exist.
+
+```bash
+serverless login
+
+# Shorthand
+sls login
+```
diff --git a/docs/providers/openwhisk/README.md b/docs/providers/openwhisk/README.md
index be09487b9..a8c78d06f 100644
--- a/docs/providers/openwhisk/README.md
+++ b/docs/providers/openwhisk/README.md
@@ -59,6 +59,7 @@ If you have questions, join the [chat in gitter](https://gitter.im/serverless/se
Deploy Function
Invoke
Invoke Local
+ Login
Logs
Info
Remove
diff --git a/docs/providers/openwhisk/cli-reference/login.md b/docs/providers/openwhisk/cli-reference/login.md
new file mode 100644
index 000000000..08bf087c2
--- /dev/null
+++ b/docs/providers/openwhisk/cli-reference/login.md
@@ -0,0 +1,24 @@
+
+
+
+### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/cli-reference/login)
+
+
+# Login
+
+The `login` command logs users into the serverless platform.
+
+It will create a new serverless platform account if one doesn't already exist.
+
+```bash
+serverless login
+
+# Shorthand
+sls login
+```
diff --git a/lib/classes/Utils.js b/lib/classes/Utils.js
index 6790e6460..a5092c59e 100644
--- a/lib/classes/Utils.js
+++ b/lib/classes/Utils.js
@@ -2,15 +2,19 @@
const fs = require('fs');
const path = require('path');
-const YAML = require('js-yaml');
const ci = require('ci-info');
const BbPromise = require('bluebird');
const fse = BbPromise.promisifyAll(require('fs-extra'));
const _ = require('lodash');
-const fetch = require('node-fetch');
const uuid = require('uuid');
const os = require('os');
+const fileExistsSync = require('../utils/fs/fileExistsSync');
+const writeFileSync = require('../utils/fs/writeFileSync');
+const readFileSync = require('../utils/fs/readFileSync');
+const isDockerContainer = require('../utils/isDockerContainer');
const version = require('../../package.json').version;
+const segment = require('../utils/segment');
+const configUtils = require('../utils/config');
class Utils {
constructor(serverless) {
@@ -31,35 +35,15 @@ class Utils {
}
fileExistsSync(filePath) {
- try {
- const stats = fse.lstatSync(filePath);
- return stats.isFile();
- } catch (e) {
- return false;
- }
+ return fileExistsSync(filePath);
}
writeFileDir(filePath) {
return fse.mkdirsSync(path.dirname(filePath));
}
- writeFileSync(filePath, conts) {
- let contents = conts || '';
-
- fse.mkdirsSync(path.dirname(filePath));
-
- if (filePath.indexOf('.json') !== -1 && typeof contents !== 'string') {
- contents = JSON.stringify(contents, null, 2);
- }
-
- const yamlFileExists = (filePath.indexOf('.yaml') !== -1);
- const ymlFileExists = (filePath.indexOf('.yml') !== -1);
-
- if ((yamlFileExists || ymlFileExists) && typeof contents !== 'string') {
- contents = YAML.dump(contents);
- }
-
- return fse.writeFileSync(filePath, contents);
+ writeFileSync(filePath, contents) {
+ return writeFileSync(filePath, contents);
}
writeFile(filePath, contents) {
@@ -88,21 +72,7 @@ class Utils {
}
readFileSync(filePath) {
- let contents;
-
- // Read file
- contents = fse.readFileSync(filePath);
-
- // Auto-parse JSON
- if (filePath.endsWith('.json')) {
- contents = JSON.parse(contents);
- } else if (filePath.endsWith('.yml') || filePath.endsWith('.yaml')) {
- contents = YAML.load(contents.toString(), { filename: filePath });
- } else {
- contents = contents.toString().trim();
- }
-
- return contents;
+ return readFileSync(filePath);
}
readFile(filePath) {
@@ -164,23 +134,15 @@ class Utils {
// the context in which serverless was executed (e.g. "install", "usage", "uninstall", ...)
context = context || 'usage'; //eslint-disable-line
- const log = (data) => {
- const writeKey = 'XXXX'; // TODO: Replace me before release
- const auth = `${writeKey}:`;
+ // Service values
+ const service = serverless.service;
+ const resources = service.resources;
+ const provider = service.provider;
+ const functions = service.functions;
- return fetch('https://api.segment.io/v1/track', {
- headers: {
- Authorization: `Basic ${new Buffer(auth).toString('base64')}`,
- 'content-type': 'application/json',
- },
- method: 'POST',
- timeout: '1000',
- body: JSON.stringify(data),
- })
- .then((response) => response.json())
- .then(() => BbPromise.resolve())
- .catch(() => BbPromise.resolve());
- };
+ // CLI inputs
+ const options = serverless.processedInput.options;
+ const commands = serverless.processedInput.commands;
return new BbPromise((resolve) => {
const serverlessDirPath = path.join(os.homedir(), '.serverless');
@@ -190,7 +152,7 @@ class Utils {
if (this.fileExistsSync(statsDisabledFilePath)) {
return resolve();
}
-
+ // Todo update with new user id
let userId = uuid.v1();
if (!this.fileExistsSync(statsEnabledFilePath)) {
@@ -200,7 +162,6 @@ class Utils {
}
// filter out the whitelisted options
- const options = serverless.processedInput.options;
const whitelistedOptionKeys = ['help', 'disable', 'enable'];
const optionKeys = Object.keys(options);
@@ -214,11 +175,11 @@ class Utils {
});
// function related information retrieval
- const numberOfFunctions = _.size(serverless.service.functions);
+ const numberOfFunctions = _.size(functions);
const memorySizeAndTimeoutPerFunction = [];
if (numberOfFunctions) {
- _.forEach(serverless.service.functions, (func) => {
+ _.forEach(functions, (func) => {
const memorySize = Number(func.memorySize)
|| Number(this.serverless.service.provider.memorySize)
|| 1024;
@@ -239,7 +200,7 @@ class Utils {
const numberOfEventsPerType = [];
const eventNamesPerFunction = [];
if (numberOfFunctions) {
- _.forEach(serverless.service.functions, (func) => {
+ _.forEach(functions, (func) => {
if (func.events) {
const funcEventsArray = [];
@@ -264,18 +225,12 @@ class Utils {
}
let hasCustomResourcesDefined = false;
-
// check if configuration in resources.Resources is defined
- if ((serverless.service.resources &&
- serverless.service.resources.Resources &&
- Object.keys(serverless.service.resources.Resources).length)) {
+ if ((resources && resources.Resources && Object.keys(resources.Resources).length)) {
hasCustomResourcesDefined = true;
}
-
// check if configuration in resources.Outputs is defined
- if ((serverless.service.resources &&
- serverless.service.resources.Outputs &&
- Object.keys(serverless.service.resources.Outputs).length)) {
+ if ((resources && resources.Outputs && Object.keys(resources.Outputs).length)) {
hasCustomResourcesDefined = true;
}
@@ -283,43 +238,32 @@ class Utils {
const defaultVariableSyntax = '\\${([ :a-zA-Z0-9._,\\-\\/\\(\\)]+?)}';
// check if the variableSyntax in the provider section is defined
- if (serverless.service.provider &&
- serverless.service.provider.variableSyntax &&
- serverless.service.provider.variableSyntax !== defaultVariableSyntax) {
+ if (provider && provider.variableSyntax
+ && provider.variableSyntax !== defaultVariableSyntax) {
hasCustomVariableSyntaxDefined = true;
}
- // wrap in try catch to make sure that missing permissions won't break anything
- let isDockerContainer = false;
- try {
- const cgroupFilePath = path.join('/', 'proc', '1', 'cgroup');
- const cgroupFileContent = fs.readFileSync(cgroupFilePath).toString();
- isDockerContainer = !!cgroupFileContent.match(/docker/);
- } catch (exception) {
- // do nothing
- }
-
const data = {
userId,
event: 'framework_stat',
properties: {
version: 2,
command: {
- name: serverless.processedInput.commands.join(' '),
+ name: commands.join(' '),
filteredOptions,
isRunInService: (!!serverless.config.servicePath),
},
service: {
- numberOfCustomPlugins: _.size(serverless.service.plugins),
+ numberOfCustomPlugins: _.size(service.plugins),
hasCustomResourcesDefined,
- hasVariablesInCustomSectionDefined: (!!serverless.service.custom),
+ hasVariablesInCustomSectionDefined: (!!service.custom),
hasCustomVariableSyntaxDefined,
},
provider: {
- name: serverless.service.provider.name,
- runtime: serverless.service.provider.runtime,
- stage: serverless.service.provider.stage,
- region: serverless.service.provider.region,
+ name: provider.name,
+ runtime: provider.runtime,
+ stage: provider.stage,
+ region: provider.region,
},
functions: {
numberOfFunctions,
@@ -339,17 +283,24 @@ class Utils {
userAgent: (process.env.SERVERLESS_DASHBOARD) ? 'dashboard' : 'cli',
serverlessVersion: serverless.version,
nodeJsVersion: process.version,
- isDockerContainer,
+ isDockerContainer: isDockerContainer(),
isCISystem: ci.isCI,
ciSystem: ci.name,
},
},
};
+ const config = configUtils.getConfig();
+ if (config.userId && data.properties && data.properties.general) {
+ data.properties.general.platformId = config.userId;
+ }
+
return resolve(data);
}).then((data) => {
// only log the data if it's there
- if (data) log(data);
+ if (data) {
+ segment.track(data);
+ }
});
}
}
diff --git a/lib/classes/Utils.test.js b/lib/classes/Utils.test.js
index 6dd47b1be..d6ee6141f 100644
--- a/lib/classes/Utils.test.js
+++ b/lib/classes/Utils.test.js
@@ -6,21 +6,22 @@ const expect = require('chai').expect;
const fse = require('fs-extra');
const fs = require('fs');
const sinon = require('sinon');
-const proxyquire = require('proxyquire');
const Serverless = require('../../lib/Serverless');
const testUtils = require('../../tests/utils');
const serverlessVersion = require('../../package.json').version;
+const segment = require('../utils/segment');
+const proxyquire = require('proxyquire');
describe('Utils', () => {
let utils;
let serverless;
- let fetchStub;
+ let isDockerContainerStub;
let Utils;
beforeEach(() => {
- fetchStub = sinon.stub().resolves();
+ isDockerContainerStub = sinon.stub().returns(true);
Utils = proxyquire('../../lib/classes/Utils.js', {
- 'node-fetch': fetchStub,
+ '../utils/isDockerContainer': isDockerContainerStub,
});
serverless = new Serverless();
utils = new Utils(serverless);
@@ -294,6 +295,7 @@ describe('Utils', () => {
describe('#logStat()', () => {
let serverlessDirPath;
let homeDir;
+ let trackStub;
beforeEach(() => {
serverless.init();
@@ -313,6 +315,12 @@ describe('Utils', () => {
// set the properties for the processed inputs
serverless.processedInput.commands = [];
serverless.processedInput.options = {};
+
+ trackStub = sinon.stub(segment, 'track');
+ });
+
+ afterEach(() => {
+ segment.track.restore();
});
it('should resolve if a file called stats-disabled is present', () => {
@@ -323,7 +331,7 @@ describe('Utils', () => {
);
return utils.logStat(serverless).then(() => {
- expect(fetchStub.calledOnce).to.equal(false);
+ expect(trackStub.calledOnce).to.equal(false);
});
});
@@ -358,36 +366,23 @@ describe('Utils', () => {
serverless.processedInput.options = options;
return utils.logStat(serverless).then(() => {
- expect(fetchStub.calledOnce).to.equal(true);
- expect(fetchStub.args[0][0]).to.equal('https://api.segment.io/v1/track');
- expect(fetchStub.args[0][1].method).to.equal('POST');
- expect(fetchStub.args[0][1].timeout).to.equal('1000');
+ expect(trackStub.calledOnce).to.equal(true);
- const parsedBody = JSON.parse(fetchStub.args[0][1].body);
+ const data = trackStub.args[0][0];
- expect(parsedBody.properties.command.filteredOptions)
- .to.deep.equal({ help: true });
+ expect(data.properties.command.filteredOptions).to.deep.equal({ help: true });
});
});
- it('should be able to detect Docker containers', () => {
- const cgroupFileContent = '6:devices:/docker/3601745b3bd54d9780436faa5f0e4f72';
- const cgroupFilePath = path.join('/', 'proc', '1', 'cgroup');
- const cgroupFileContentStub = sinon.stub(fs, 'readFileSync')
- .withArgs(cgroupFilePath)
- .returns(cgroupFileContent);
+ it('should be able to detect Docker containers', () => utils
+ .logStat(serverless).then(() => {
+ expect(isDockerContainerStub.calledOnce).to.equal(true);
+ expect(trackStub.calledOnce).to.equal(true);
- utils.logStat(serverless).then(() => {
- expect(cgroupFileContentStub.calledOnce).to.equal(true);
-
- const parsedBody = JSON.parse(fetchStub.args[0][1].body);
-
- expect(parsedBody.properties.general.isDockerContainer)
- .to.equal(true);
-
- fs.readFileSync.restore();
- });
- });
+ const data = trackStub.args[0][0];
+ expect(data.properties.general.isDockerContainer).to.equal(true);
+ })
+ );
it('should send the gathered information', () => {
serverless.service = {
@@ -436,55 +431,52 @@ describe('Utils', () => {
};
return utils.logStat(serverless).then(() => {
- expect(fetchStub.calledOnce).to.equal(true);
- expect(fetchStub.args[0][0]).to.equal('https://api.segment.io/v1/track');
- expect(fetchStub.args[0][1].method).to.equal('POST');
- expect(fetchStub.args[0][1].timeout).to.equal('1000');
+ expect(trackStub.calledOnce).to.equal(true);
- const parsedBody = JSON.parse(fetchStub.args[0][1].body);
+ const data = trackStub.args[0][0];
- expect(parsedBody.userId.length).to.be.at.least(1);
+ expect(data.userId.length).to.be.at.least(1);
// command property
- expect(parsedBody.properties.command.name)
+ expect(data.properties.command.name)
.to.equal('');
- expect(parsedBody.properties.command
+ expect(data.properties.command
.isRunInService).to.equal(false); // false because CWD is not a service
- expect(parsedBody.properties.command.filteredOptions)
+ expect(data.properties.command.filteredOptions)
.to.deep.equal({});
// service property
- expect(parsedBody.properties.service.numberOfCustomPlugins).to.equal(0);
- expect(parsedBody.properties.service.hasCustomResourcesDefined).to.equal(true);
- expect(parsedBody.properties.service.hasVariablesInCustomSectionDefined).to.equal(false);
- expect(parsedBody.properties.service.hasCustomVariableSyntaxDefined).to.equal(true);
+ expect(data.properties.service.numberOfCustomPlugins).to.equal(0);
+ expect(data.properties.service.hasCustomResourcesDefined).to.equal(true);
+ expect(data.properties.service.hasVariablesInCustomSectionDefined).to.equal(false);
+ expect(data.properties.service.hasCustomVariableSyntaxDefined).to.equal(true);
// functions property
- expect(parsedBody.properties.functions.numberOfFunctions).to.equal(2);
- expect(parsedBody.properties.functions.memorySizeAndTimeoutPerFunction[0]
+ expect(data.properties.functions.numberOfFunctions).to.equal(2);
+ expect(data.properties.functions.memorySizeAndTimeoutPerFunction[0]
.memorySize).to.equal(1024);
- expect(parsedBody.properties.functions.memorySizeAndTimeoutPerFunction[0]
+ expect(data.properties.functions.memorySizeAndTimeoutPerFunction[0]
.timeout).to.equal(6);
- expect(parsedBody.properties.functions.memorySizeAndTimeoutPerFunction[1]
+ expect(data.properties.functions.memorySizeAndTimeoutPerFunction[1]
.memorySize).to.equal(16);
- expect(parsedBody.properties.functions.memorySizeAndTimeoutPerFunction[1]
+ expect(data.properties.functions.memorySizeAndTimeoutPerFunction[1]
.timeout).to.equal(200);
// events property
- expect(parsedBody.properties.events.numberOfEvents).to.equal(3);
- expect(parsedBody.properties.events.numberOfEventsPerType[0].name).to.equal('http');
- expect(parsedBody.properties.events.numberOfEventsPerType[0].count).to.equal(2);
- expect(parsedBody.properties.events.numberOfEventsPerType[1].name).to.equal('s3');
- expect(parsedBody.properties.events.numberOfEventsPerType[1].count).to.equal(1);
- expect(parsedBody.properties.events.numberOfEventsPerType[2].name).to.equal('sns');
- expect(parsedBody.properties.events.numberOfEventsPerType[2].count).to.equal(1);
- expect(parsedBody.properties.events.eventNamesPerFunction[0][0]).to.equal('http');
- expect(parsedBody.properties.events.eventNamesPerFunction[0][1]).to.equal('s3');
- expect(parsedBody.properties.events.eventNamesPerFunction[1][0]).to.equal('http');
- expect(parsedBody.properties.events.eventNamesPerFunction[1][1]).to.equal('sns');
+ expect(data.properties.events.numberOfEvents).to.equal(3);
+ expect(data.properties.events.numberOfEventsPerType[0].name).to.equal('http');
+ expect(data.properties.events.numberOfEventsPerType[0].count).to.equal(2);
+ expect(data.properties.events.numberOfEventsPerType[1].name).to.equal('s3');
+ expect(data.properties.events.numberOfEventsPerType[1].count).to.equal(1);
+ expect(data.properties.events.numberOfEventsPerType[2].name).to.equal('sns');
+ expect(data.properties.events.numberOfEventsPerType[2].count).to.equal(1);
+ expect(data.properties.events.eventNamesPerFunction[0][0]).to.equal('http');
+ expect(data.properties.events.eventNamesPerFunction[0][1]).to.equal('s3');
+ expect(data.properties.events.eventNamesPerFunction[1][0]).to.equal('http');
+ expect(data.properties.events.eventNamesPerFunction[1][1]).to.equal('sns');
// general property
- expect(parsedBody.properties.general.userId.length).to.be.at.least(1);
- expect(parsedBody.properties.general.timestamp).to.match(/[0-9]+/);
- expect(parsedBody.properties.general.timezone.length).to.be.at.least(1);
- expect(parsedBody.properties.general.operatingSystem.length).to.be.at.least(1);
- expect(parsedBody.properties.general.serverlessVersion).to.equal(serverlessVersion);
- expect(parsedBody.properties.general.nodeJsVersion.length).to.be.at.least(1);
+ expect(data.properties.general.userId.length).to.be.at.least(1);
+ expect(data.properties.general.timestamp).to.match(/[0-9]+/);
+ expect(data.properties.general.timezone.length).to.be.at.least(1);
+ expect(data.properties.general.operatingSystem.length).to.be.at.least(1);
+ expect(data.properties.general.serverlessVersion).to.equal(serverlessVersion);
+ expect(data.properties.general.nodeJsVersion.length).to.be.at.least(1);
});
});
diff --git a/lib/plugins/Plugins.json b/lib/plugins/Plugins.json
index af4fee885..48b655570 100644
--- a/lib/plugins/Plugins.json
+++ b/lib/plugins/Plugins.json
@@ -8,6 +8,8 @@
"./invoke/invoke.js",
"./info/info.js",
"./logs/logs.js",
+ "./login/login.js",
+ "./logout/logout.js",
"./metrics/metrics.js",
"./remove/remove.js",
"./rollback/index.js",
diff --git a/lib/plugins/create/create.js b/lib/plugins/create/create.js
index 8d20aaf3b..7851a43a8 100644
--- a/lib/plugins/create/create.js
+++ b/lib/plugins/create/create.js
@@ -4,6 +4,7 @@ const BbPromise = require('bluebird');
const path = require('path');
const fse = require('fs-extra');
const _ = require('lodash');
+const userStats = require('../../utils/userStats');
// class wide constants
const validTemplates = [
@@ -117,7 +118,9 @@ class Create {
});
}
- if (notPlugin) this.serverless.config.update({ servicePath: process.cwd() });
+ if (notPlugin) {
+ this.serverless.config.update({ servicePath: process.cwd() });
+ }
// copy template files recursively to cwd
// while keeping template file tree
@@ -152,6 +155,11 @@ class Create {
fse.writeFileSync(serverlessYmlFilePath, serverlessYmlFileContent);
}
+ userStats.track('service_created', {
+ template: this.options.template,
+ serviceName,
+ });
+
this.serverless.cli.asciiGreeting();
this.serverless.cli
.log(`Successfully generated boilerplate for template: "${this.options.template}"`);
diff --git a/lib/plugins/deploy/deploy.js b/lib/plugins/deploy/deploy.js
index dde9ce0de..fc470e51f 100644
--- a/lib/plugins/deploy/deploy.js
+++ b/lib/plugins/deploy/deploy.js
@@ -1,6 +1,7 @@
'use strict';
const BbPromise = require('bluebird');
+const userStats = require('../../utils/userStats');
class Deploy {
constructor(serverless, options) {
@@ -88,8 +89,13 @@ class Deploy {
}
return BbPromise.resolve();
}),
+ 'after:deploy:deploy': () => BbPromise.bind(this)
+ .then(this.track),
};
}
+ track() {
+ userStats.track('service_deployed');
+ }
}
module.exports = Deploy;
diff --git a/lib/plugins/login/login.js b/lib/plugins/login/login.js
new file mode 100644
index 000000000..902164475
--- /dev/null
+++ b/lib/plugins/login/login.js
@@ -0,0 +1,159 @@
+'use strict';
+
+const BbPromise = require('bluebird');
+const crypto = require('crypto');
+const readline = require('readline');
+const fetch = require('node-fetch');
+const jwtDecode = require('jwt-decode');
+const chalk = require('chalk');
+const openBrowser = require('../../utils/openBrowser');
+const getFrameworkId = require('../../utils/getFrameworkId');
+const clearConsole = require('../../utils/clearConsole');
+const userStats = require('../../utils/userStats');
+const setConfig = require('../../utils/config').set;
+
+const config = {
+ AUTH0_CLIENT_ID: 'iiEYK0KB30gj94mjB8HP9lhhTgae0Rg3',
+ AUTH0_URL: 'https://serverlessinc.auth0.com',
+ AUTH0_CALLBACK_URL: 'https://serverless.com/auth',
+};
+
+function base64url(url) {
+ return url.toString('base64')
+ .replace(/\+/g, '-')
+ .replace(/\//g, '_')
+ .replace(/=/g, '');
+}
+
+class Login {
+ constructor(serverless, options) {
+ this.serverless = serverless;
+ this.options = options;
+
+ this.commands = {
+ login: {
+ usage: 'Login or sign up for the Serverless Platform',
+ lifecycleEvents: [
+ 'login',
+ ],
+ },
+ };
+
+ this.hooks = {
+ 'login:login': () => BbPromise.bind(this).then(this.login),
+ };
+ }
+ login() {
+ clearConsole();
+ this.serverless.cli.log('The Serverless login will open in your default browser...');
+ // Generate the verifier, and the corresponding challenge
+ const verifier = base64url(crypto.randomBytes(32));
+ const verifierChallenge = base64url(crypto.createHash('sha256').update(verifier).digest());
+ const frameworkId = getFrameworkId();
+ // eslint-disable-next-line prefer-template
+ const version = this.serverless.version;
+ const state = `id%3D${frameworkId}%26version%3D${version}%26platform%3D${process.platform}`;
+ // refresh token docs https://auth0.com/docs/tokens/preview/refresh-token#get-a-refresh-token
+ const authorizeUrl =
+ `${config.AUTH0_URL}/authorize?response_type=code&scope=openid%20profile%20offline_access` +
+ `&client_id=${config.AUTH0_CLIENT_ID}&redirect_uri=${config.AUTH0_CALLBACK_URL}` +
+ `&code_challenge=${verifierChallenge}&code_challenge_method=S256&state=${state}`;
+
+ setTimeout(() => {
+ this.serverless.cli.log('Opening browser...');
+ }, 300);
+
+ setTimeout(() => {
+ // pop open default browser
+ openBrowser(authorizeUrl);
+
+ // wait for token
+ const readlineInterface = readline.createInterface({
+ input: process.stdin,
+ output: process.stdout,
+ });
+ // o get an access token and a refresh token from that code you need to call the oauth/token endpoint. Here's more info - https://auth0.com/docs/protocols#3-getting-the-access-token
+ readlineInterface.question('Please enter the verification code here: ', (code) => {
+ const authorizationData = {
+ code,
+ code_verifier: verifier,
+ client_id: config.AUTH0_CLIENT_ID,
+ grant_type: 'authorization_code',
+ redirect_uri: config.AUTH0_CALLBACK_URL,
+ };
+ // verify login
+ fetch(`${config.AUTH0_URL}/oauth/token`, {
+ method: 'POST',
+ body: JSON.stringify(authorizationData),
+ headers: { 'content-type': 'application/json' },
+ })
+ .then((response) => response.json())
+ .then((platformResponse) => {
+ const decoded = jwtDecode(platformResponse.id_token);
+ this.serverless.cli.log('You are now logged in');
+
+ // because platform only support github
+ const id = decoded.original_user_id || decoded.sub;
+
+ /* For future use
+ segment.identify({
+ userId: id,
+ traits: {
+ email: profile.email,
+ },
+ }) */
+
+ const userConfig = {
+ userId: id,
+ frameworkId,
+ users: {},
+ };
+ // set user auth in global .serverlessrc file
+ userConfig.users[id] = {
+ userId: id,
+ name: decoded.name,
+ email: decoded.email,
+ auth: platformResponse,
+ };
+
+ // update .serverlessrc
+ setConfig(userConfig);
+
+ const userID = new Buffer(id).toString('base64');
+ const email = new Buffer(decoded.email).toString('base64');
+ const name = new Buffer(decoded.name).toString('base64');
+ const loginCount = decoded.login_count;
+ const createdAt = decoded.created_at;
+ const successUrl = `https://serverless.com/success?u=${userID}&e=${email}&n=${name}&c=${loginCount}&v=${version}&d=${createdAt}&id=${frameworkId}`; // eslint-disable-line
+
+ openBrowser(successUrl);
+ // identify user for better onboarding
+ userStats.identify({
+ id,
+ frameworkId,
+ email: decoded.email,
+ // unix timestamp
+ created_at: Math.round(+new Date(createdAt) / 1000),
+ force: true,
+ }).then(() => {
+ userStats.track('user_loggedIn', {
+ id,
+ email: decoded.email,
+ force: true,
+ }).then(() => {
+ // then exit process
+ process.exit(0);
+ });
+ });
+ })
+ .catch(() => {
+ this.serverless.cli.consoleLog(
+ chalk.red('Incorrect token value supplied. Please run "serverless login" again'));
+ process.exit(0);
+ });
+ });
+ }, 2000);
+ }
+}
+
+module.exports = Login;
diff --git a/lib/plugins/login/login.test.js b/lib/plugins/login/login.test.js
new file mode 100644
index 000000000..72c716fa4
--- /dev/null
+++ b/lib/plugins/login/login.test.js
@@ -0,0 +1,19 @@
+'use strict';
+
+const expect = require('chai').expect;
+const Login = require('./login');
+const Serverless = require('../../Serverless');
+
+describe('Login', () => {
+ let login;
+ let serverless;
+
+ beforeEach(() => {
+ serverless = new Serverless();
+ login = new Login(serverless);
+ });
+
+ describe('#constructor()', () => {
+ it('should have commands', () => expect(login.commands).to.be.not.empty);
+ });
+});
diff --git a/lib/plugins/logout/logout.js b/lib/plugins/logout/logout.js
new file mode 100644
index 000000000..c362275bb
--- /dev/null
+++ b/lib/plugins/logout/logout.js
@@ -0,0 +1,53 @@
+'use strict';
+
+const userStats = require('../../utils/userStats');
+const configUtils = require('../../utils/config');
+
+class Login {
+ constructor(serverless, options) {
+ this.serverless = serverless;
+ this.options = options;
+
+ this.commands = {
+ logout: {
+ usage: 'Logout from the Serverless Platform',
+ lifecycleEvents: ['logout'],
+ },
+ };
+
+ this.hooks = {
+ 'logout:logout': this.logout.bind(this),
+ };
+ }
+ logout() {
+ const config = configUtils.getConfig();
+ const currentId = config.userId;
+ const globalConfig = configUtils.getGlobalConfig();
+
+ try {
+ // TODO Once we start using refresh tokens we also need to implement an API endpoint
+ // that invalidate a refresh token in Auth0 (using the Auth0 Management API).
+
+ if (globalConfig && globalConfig.users && globalConfig.users[currentId]) {
+ if (globalConfig.users[currentId].auth) {
+ // remove auth tokens from user
+ configUtils.set(`users.${currentId}.auth`, null);
+ // log stat
+ userStats.track('user_loggedOut').then(() => {
+ this.serverless.cli.consoleLog('Successfully logged out.');
+ process.exit(0);
+ });
+ } else {
+ this.serverless.cli.consoleLog('You are already logged out');
+ }
+ }
+ } catch (e) {
+ this.serverless.cli.consoleLog(
+ 'Failed to logout. Please report bug in https://github.com/serverless/serverless/issues');
+ // Note no need to wait for any connections e.g. segment to close
+ process.exit(0);
+ }
+ }
+}
+
+module.exports = Login;
diff --git a/lib/plugins/logout/logout.test.js b/lib/plugins/logout/logout.test.js
new file mode 100644
index 000000000..4df6b059d
--- /dev/null
+++ b/lib/plugins/logout/logout.test.js
@@ -0,0 +1,19 @@
+'use strict';
+
+const expect = require('chai').expect;
+const Logout = require('./logout');
+const Serverless = require('../../Serverless');
+
+describe('Logout', () => {
+ let logout;
+ let serverless;
+
+ beforeEach(() => {
+ serverless = new Serverless();
+ logout = new Logout(serverless);
+ });
+
+ describe('#constructor()', () => {
+ it('should have commands', () => expect(logout.commands).to.be.not.empty);
+ });
+});
diff --git a/lib/plugins/slstats/slstats.js b/lib/plugins/slstats/slstats.js
index 5301826d0..ac0dd688a 100644
--- a/lib/plugins/slstats/slstats.js
+++ b/lib/plugins/slstats/slstats.js
@@ -3,6 +3,8 @@
const path = require('path');
const fse = require('fs-extra');
const os = require('os');
+const userStats = require('../../utils/userStats');
+const setConfig = require('../../utils/config').set;
class SlStats {
constructor(serverless, options) {
@@ -53,10 +55,20 @@ class SlStats {
const isStatsEnabled = this.options.enable && !this.options.disable;
const isStatsDisabled = this.options.disable && !this.options.enable;
if (isStatsEnabled) {
+ // stats file to be depricated in future release
this.createStatsFile(statsDisabledFilePath, statsEnabledFilePath);
+ // set new `trackingDisabled` key in .serverlessrc config
+ userStats.track('user_enabledTracking', { force: true }).then(() => {
+ setConfig('trackingDisabled', false);
+ });
this.serverless.cli.log('Stats successfully enabled');
} else if (isStatsDisabled) {
+ // stats file to be depricated in future release
this.createStatsFile(statsEnabledFilePath, statsDisabledFilePath);
+ // set new `trackingDisabled` key in .serverlessrc config
+ userStats.track('user_disabledTracking', { force: true }).then(() => {
+ setConfig('trackingDisabled', true);
+ });
this.serverless.cli.log('Stats successfully disabled');
}
} catch (error) {
diff --git a/lib/utils/clearConsole.js b/lib/utils/clearConsole.js
new file mode 100644
index 000000000..aeb5b9f22
--- /dev/null
+++ b/lib/utils/clearConsole.js
@@ -0,0 +1,8 @@
+'use strict';
+
+/* Clear terminal output */
+const clearConsole = function () {
+ process.stdout.write(process.platform !== 'win32' ? '\x1B[2J\x1B[3J\x1B[H' : '\x1Bc');
+};
+
+module.exports = clearConsole;
diff --git a/lib/utils/config/config.test.js b/lib/utils/config/config.test.js
new file mode 100644
index 000000000..7ed4d1e46
--- /dev/null
+++ b/lib/utils/config/config.test.js
@@ -0,0 +1,54 @@
+'use strict';
+
+const expect = require('chai').expect;
+const config = require('./index');
+
+describe('Config', () => {
+ it('should have CONFIG_FILE_PATH', () => {
+ const configPath = config.CONFIG_FILE_PATH;
+ expect(configPath).to.exist; // eslint-disable-line
+ });
+
+ describe('When using config.getConfig', () => {
+ it('should have userId key', () => {
+ const conf = config.getConfig();
+ expect(conf).to.have.deep.property('userId');
+ });
+
+ it('should have frameworkId key', () => {
+ const conf = config.getConfig();
+ expect(conf).to.have.deep.property('frameworkId');
+ });
+
+ it('should have trackingDisabled key', () => {
+ const conf = config.getConfig();
+ expect(conf).to.have.deep.property('trackingDisabled');
+ });
+ });
+
+ describe('When using config.get', () => {
+ it('should have frameworkId', () => {
+ const frameworkId = config.get('frameworkId');
+ expect(frameworkId).to.exist; // eslint-disable-line
+ });
+ it('should have not have a value that doesnt exist', () => {
+ const doesntExist = config.get('frameworkIdzzzz');
+ expect(doesntExist).to.not.exist; // eslint-disable-line
+ });
+ });
+
+ describe('When using config.set', () => {
+ it('should add new properties with "set"', () => {
+ config.set('foo', true);
+ const foo = config.get('foo');
+ expect(foo).to.equal(true);
+ });
+
+ it('should delete properties with "delete"', () => {
+ // cleanup foo
+ config.delete('foo');
+ const zaz = config.get('foo');
+ expect(zaz).to.equal(undefined);
+ });
+ });
+});
diff --git a/lib/utils/config/index.js b/lib/utils/config/index.js
new file mode 100644
index 000000000..53906f08b
--- /dev/null
+++ b/lib/utils/config/index.js
@@ -0,0 +1,96 @@
+'use strict';
+
+/* Config util */
+const path = require('path');
+const os = require('os');
+const _ = require('lodash');
+const writeFileAtomic = require('write-file-atomic');
+const getFrameworkId = require('../getFrameworkId');
+const fileExistsSync = require('../fs/fileExistsSync');
+const readFileSync = require('../fs/readFileSync');
+const isTrackingDisabled = require('../isTrackingDisabled');
+
+const globalConfigPath = path.join(os.homedir(), '.serverlessrc');
+
+function createConfig() {
+ // set default config options
+ const config = {
+ userId: null, // currentUserId
+ frameworkId: getFrameworkId(),
+ trackingDisabled: isTrackingDisabled(),
+ meta: {
+ created_at: Math.round(+new Date() / 1000),
+ updated_at: null,
+ },
+ };
+ writeFileAtomic.sync(globalConfigPath, JSON.stringify(config, null, 2));
+ return JSON.parse(readFileSync(globalConfigPath));
+}
+
+function getGlobalConfig() {
+ if (!fileExistsSync(globalConfigPath)) {
+ return createConfig();
+ }
+ const config = readFileSync(globalConfigPath);
+ // if global config empty add defaults
+ if (!config) {
+ return createConfig();
+ }
+ return JSON.parse(config);
+}
+
+// get .serverlessrc config + local config if exists
+function getConfig() {
+ if (!fileExistsSync(globalConfigPath)) {
+ return createConfig();
+ }
+ return require('rc')('serverless'); // eslint-disable-line
+}
+
+// set global .serverlessrc config value.
+function setConfigValue(key, value) {
+ let config = getGlobalConfig();
+ if (key && typeof key === 'string' && typeof value !== 'undefined') {
+ config = _.set(config, key, value);
+ } else if (_.isObject(key)) {
+ config = _.merge(config, key);
+ } else if (typeof value !== 'undefined') {
+ config = _.merge(config, value);
+ }
+ // update config meta
+ config.meta = config.meta || {};
+ config.meta.updated_at = Math.round(+new Date() / 1000);
+ // write file
+ writeFileAtomic.sync(globalConfigPath, JSON.stringify(config, null, 2));
+ return config;
+}
+
+function deleteConfigValue(key) {
+ let config = getGlobalConfig();
+ if (key && typeof key === 'string') {
+ config = _.omit(config, [key]);
+ } else if (key && _.isArray(key)) {
+ config = _.omit(config, key);
+ }
+ // write file
+ writeFileAtomic.sync(globalConfigPath, JSON.stringify(config, null, 2));
+ return config;
+}
+
+// set .serverlessrc config value
+function getConfigValue(objectPath) {
+ const config = getConfig();
+ if (objectPath && typeof objectPath === 'string') {
+ return _.get(config, objectPath);
+ }
+ return config;
+}
+
+module.exports = {
+ set: setConfigValue,
+ get: getConfigValue,
+ delete: deleteConfigValue,
+ getConfig,
+ getGlobalConfig,
+ CONFIG_FILE_PATH: globalConfigPath,
+};
diff --git a/lib/utils/fs/fileExistsSync.js b/lib/utils/fs/fileExistsSync.js
new file mode 100644
index 000000000..147300e7b
--- /dev/null
+++ b/lib/utils/fs/fileExistsSync.js
@@ -0,0 +1,14 @@
+'use strict';
+
+const fse = require('./fse');
+
+function fileExistsSync(filePath) {
+ try {
+ const stats = fse.lstatSync(filePath);
+ return stats.isFile();
+ } catch (e) {
+ return false;
+ }
+}
+
+module.exports = fileExistsSync;
diff --git a/lib/utils/fs/fileExistsSync.test.js b/lib/utils/fs/fileExistsSync.test.js
new file mode 100644
index 000000000..34ab7fefe
--- /dev/null
+++ b/lib/utils/fs/fileExistsSync.test.js
@@ -0,0 +1,19 @@
+'use strict';
+
+const path = require('path');
+const expect = require('chai').expect;
+const fileExistsSync = require('./fileExistsSync');
+
+describe('#fileExistsSync()', () => {
+ describe('When reading a file', () => {
+ it('should detect if a file exists', () => {
+ const file = fileExistsSync(__filename);
+ expect(file).to.equal(true);
+ });
+
+ it('should detect if a file doesn\'t exist', () => {
+ const noFile = fileExistsSync(path.join(__dirname, 'XYZ.json'));
+ expect(noFile).to.equal(false);
+ });
+ });
+});
diff --git a/lib/utils/fs/fse.js b/lib/utils/fs/fse.js
new file mode 100644
index 000000000..c1428e286
--- /dev/null
+++ b/lib/utils/fs/fse.js
@@ -0,0 +1,9 @@
+/**
+ * Promisified FSE
+ */
+'use strict';
+
+const BbPromise = require('bluebird');
+const fse = BbPromise.promisifyAll(require('fs-extra'));
+
+module.exports = fse;
diff --git a/lib/utils/fs/readFileSync.js b/lib/utils/fs/readFileSync.js
new file mode 100644
index 000000000..7d341b4c7
--- /dev/null
+++ b/lib/utils/fs/readFileSync.js
@@ -0,0 +1,24 @@
+'use strict';
+
+const YAML = require('js-yaml');
+const fse = require('./fse');
+
+function readFileSync(filePath) {
+ let contents;
+
+ // Read file
+ contents = fse.readFileSync(filePath);
+
+ // Auto-parse JSON
+ if (filePath.endsWith('.json')) {
+ contents = JSON.parse(contents);
+ } else if (filePath.endsWith('.yml') || filePath.endsWith('.yaml')) {
+ contents = YAML.load(contents.toString(), { filename: filePath });
+ } else {
+ contents = contents.toString().trim();
+ }
+
+ return contents;
+}
+
+module.exports = readFileSync;
diff --git a/lib/utils/fs/readFileSync.test.js b/lib/utils/fs/readFileSync.test.js
new file mode 100644
index 000000000..652432040
--- /dev/null
+++ b/lib/utils/fs/readFileSync.test.js
@@ -0,0 +1,45 @@
+'use strict';
+
+const testUtils = require('../../../tests/utils');
+const expect = require('chai').expect;
+const writeFileSync = require('./writeFileSync');
+const readFileSync = require('./readFileSync');
+
+describe('#readFileSync()', () => {
+ it('should read a file synchronously', () => {
+ const tmpFilePath = testUtils.getTmpFilePath('anything.json');
+
+ writeFileSync(tmpFilePath, { foo: 'bar' });
+ const obj = readFileSync(tmpFilePath);
+
+ expect(obj.foo).to.equal('bar');
+ });
+
+ it('should read a filename extension .yml', () => {
+ const tmpFilePath = testUtils.getTmpFilePath('anything.yml');
+
+ writeFileSync(tmpFilePath, { foo: 'bar' });
+ const obj = readFileSync(tmpFilePath);
+
+ expect(obj.foo).to.equal('bar');
+ });
+
+ it('should read a filename extension .yaml', () => {
+ const tmpFilePath = testUtils.getTmpFilePath('anything.yaml');
+
+ writeFileSync(tmpFilePath, { foo: 'bar' });
+ const obj = readFileSync(tmpFilePath);
+
+ expect(obj.foo).to.equal('bar');
+ });
+
+ it('should throw YAMLException with filename if yml file is invalid format', () => {
+ const tmpFilePath = testUtils.getTmpFilePath('invalid.yml');
+
+ writeFileSync(tmpFilePath, ': a');
+
+ expect(() => {
+ readFileSync(tmpFilePath);
+ }).to.throw(new RegExp('YAMLException:.*invalid.yml'));
+ });
+});
diff --git a/lib/utils/fs/writeFileSync.js b/lib/utils/fs/writeFileSync.js
new file mode 100644
index 000000000..e9fefae59
--- /dev/null
+++ b/lib/utils/fs/writeFileSync.js
@@ -0,0 +1,26 @@
+'use strict';
+
+const path = require('path');
+const YAML = require('js-yaml');
+const fse = require('./fse');
+
+function writeFileSync(filePath, conts) {
+ let contents = conts || '';
+
+ fse.mkdirsSync(path.dirname(filePath));
+
+ if (filePath.indexOf('.json') !== -1 && typeof contents !== 'string') {
+ contents = JSON.stringify(contents, null, 2);
+ }
+
+ const yamlFileExists = (filePath.indexOf('.yaml') !== -1);
+ const ymlFileExists = (filePath.indexOf('.yml') !== -1);
+
+ if ((yamlFileExists || ymlFileExists) && typeof contents !== 'string') {
+ contents = YAML.dump(contents);
+ }
+
+ return fse.writeFileSync(filePath, contents);
+}
+
+module.exports = writeFileSync;
diff --git a/lib/utils/fs/writeFileSync.test.js b/lib/utils/fs/writeFileSync.test.js
new file mode 100644
index 000000000..5a685e263
--- /dev/null
+++ b/lib/utils/fs/writeFileSync.test.js
@@ -0,0 +1,49 @@
+'use strict';
+
+const testUtils = require('../../../tests/utils');
+const Serverless = require('../../../lib/Serverless');
+const expect = require('chai').expect;
+const writeFileSync = require('./writeFileSync');
+const readFileSync = require('./readFileSync');
+
+describe('#writeFileSync()', () => {
+ let serverless;
+
+ beforeEach(() => {
+ serverless = new Serverless();
+ serverless.init();
+ });
+
+ it('should write a .json file synchronously', () => {
+ const tmpFilePath = testUtils.getTmpFilePath('anything.json');
+
+ writeFileSync(tmpFilePath, { foo: 'bar' });
+ const obj = readFileSync(tmpFilePath);
+
+ expect(obj.foo).to.equal('bar');
+ });
+
+ it('should write a .yml file synchronously', () => {
+ const tmpFilePath = testUtils.getTmpFilePath('anything.yml');
+
+ writeFileSync(tmpFilePath, { foo: 'bar' });
+
+ return serverless.yamlParser.parse(tmpFilePath).then((obj) => {
+ expect(obj.foo).to.equal('bar');
+ });
+ });
+
+ it('should write a .yaml file synchronously', () => {
+ const tmpFilePath = testUtils.getTmpFilePath('anything.yaml');
+
+ writeFileSync(tmpFilePath, { foo: 'bar' });
+
+ return serverless.yamlParser.parse(tmpFilePath).then((obj) => {
+ expect(obj.foo).to.equal('bar');
+ });
+ });
+
+ it('should throw error if invalid path is provided', () => {
+ expect(() => { writeFileSync(null); }).to.throw(Error);
+ });
+});
diff --git a/lib/utils/getFrameworkId.js b/lib/utils/getFrameworkId.js
new file mode 100644
index 000000000..db940400d
--- /dev/null
+++ b/lib/utils/getFrameworkId.js
@@ -0,0 +1,29 @@
+'use strict';
+
+const path = require('path');
+const getServerlessDir = require('./getServerlessDir');
+const readFileSync = require('./fs/readFileSync');
+const fileExistsSync = require('./fs/fileExistsSync');
+
+function getFrameworkId() {
+ const serverlessHomePath = getServerlessDir();
+ const statsEnabledFilePath = path.join(serverlessHomePath, 'stats-enabled');
+ const statsDisabledFilePath = path.join(serverlessHomePath, 'stats-disabled');
+ const serverlessRCFilePath = path.join(serverlessHomePath, '.serverlessrc');
+
+ if (fileExistsSync(statsEnabledFilePath)) {
+ return readFileSync(statsEnabledFilePath).toString();
+ }
+ if (fileExistsSync(statsDisabledFilePath)) {
+ return readFileSync(statsDisabledFilePath).toString();
+ }
+ if (fileExistsSync(serverlessRCFilePath)) {
+ const config = JSON.parse(readFileSync(serverlessRCFilePath));
+ if (config && config.frameworkId) {
+ return config.frameworkId;
+ }
+ }
+ return null;
+}
+
+module.exports = getFrameworkId;
diff --git a/lib/utils/getServerlessDir.js b/lib/utils/getServerlessDir.js
new file mode 100644
index 000000000..d38e63878
--- /dev/null
+++ b/lib/utils/getServerlessDir.js
@@ -0,0 +1,11 @@
+'use strict';
+
+const path = require('path');
+const os = require('os');
+
+// get .serverless home path
+function getServerlessDir() {
+ return path.join(os.homedir(), '.serverless');
+}
+
+module.exports = getServerlessDir;
diff --git a/lib/utils/isDockerContainer.js b/lib/utils/isDockerContainer.js
new file mode 100644
index 000000000..57e89b282
--- /dev/null
+++ b/lib/utils/isDockerContainer.js
@@ -0,0 +1,20 @@
+'use strict';
+
+const path = require('path');
+const readFileSync = require('./fs/readFileSync');
+const fileExistsSync = require('./fs/fileExistsSync');
+
+/* Check if is inside docker container */
+module.exports = function isDockerContainer() {
+ // wrap in try catch to make sure that missing permissions won't break anything
+ try {
+ const cgroupFilePath = path.join('/', 'proc', '1', 'cgroup');
+ if (fileExistsSync(cgroupFilePath)) {
+ const cgroupFileContent = readFileSync(cgroupFilePath).toString();
+ return !!cgroupFileContent.match(/docker/);
+ }
+ } catch (exception) {
+ // do nothing
+ }
+ return false;
+};
diff --git a/lib/utils/isTrackingDisabled.js b/lib/utils/isTrackingDisabled.js
new file mode 100644
index 000000000..09e349b4b
--- /dev/null
+++ b/lib/utils/isTrackingDisabled.js
@@ -0,0 +1,13 @@
+'use strict';
+
+const path = require('path');
+const fileExistsSync = require('./fs/fileExistsSync');
+const getServerlessDir = require('./getServerlessDir');
+
+module.exports = function isTrackingDisabled() {
+ // to be updated to .serverlessrc
+ if (fileExistsSync(path.join(getServerlessDir(), 'stats-disabled'))) {
+ return true;
+ }
+ return false;
+};
diff --git a/lib/utils/openBrowser.js b/lib/utils/openBrowser.js
new file mode 100644
index 000000000..bc419d194
--- /dev/null
+++ b/lib/utils/openBrowser.js
@@ -0,0 +1,34 @@
+'use strict';
+
+/* eslint-disable no-console */
+
+const opn = require('opn');
+const chalk = require('chalk');
+const isDockerContainer = require('./isDockerContainer');
+
+function displayManualOpenMessage(url) {
+ // https://github.com/sindresorhus/log-symbols
+ console.log('---------------------------');
+ console.log(`🙈 ${chalk.red('Unable to open browser automatically')}`);
+ console.log(chalk.green('Please open your browser & open the URL below to login:'));
+ console.log(chalk.yellow(url));
+ console.log('---------------------------');
+ return false;
+}
+
+module.exports = function openBrowser(url) {
+ let browser = process.env.BROWSER;
+ if (browser === 'none' || isDockerContainer()) {
+ return displayManualOpenMessage(url);
+ }
+ if (process.platform === 'darwin' && browser === 'open') {
+ browser = undefined;
+ }
+ try {
+ const options = { app: browser };
+ opn(url, options).catch(() => {});
+ return true;
+ } catch (err) {
+ return displayManualOpenMessage(url);
+ }
+};
diff --git a/lib/utils/segment.js b/lib/utils/segment.js
new file mode 100644
index 000000000..2e81047d1
--- /dev/null
+++ b/lib/utils/segment.js
@@ -0,0 +1,37 @@
+'use strict';
+
+const BbPromise = require('bluebird');
+const fetch = require('node-fetch');
+const isTrackingDisabled = require('./isTrackingDisabled');
+// TODO: Replace me before release
+const writeKey = 'XXXX';
+const auth = `${writeKey}:`;
+const TRACKING_IS_DISABLED = isTrackingDisabled();
+
+/* note segment call swallows errors */
+function request(url, payload) {
+ return fetch(url, {
+ headers: {
+ Authorization: `Basic ${new Buffer(auth).toString('base64')}`,
+ 'content-type': 'application/json',
+ },
+ method: 'POST',
+ timeout: '1000',
+ body: JSON.stringify(payload),
+ })
+ .then((response) => response.json())
+ .then(() => BbPromise.resolve())
+ .catch(() => BbPromise.resolve());
+}
+
+function track(payload) {
+ // exit early is tracking disabled
+ if (TRACKING_IS_DISABLED) {
+ return BbPromise.resolve();
+ }
+ return request('https://api.segment.io/v1/track', payload);
+}
+
+module.exports = {
+ track,
+};
diff --git a/lib/utils/userStats.js b/lib/utils/userStats.js
new file mode 100644
index 000000000..962465e68
--- /dev/null
+++ b/lib/utils/userStats.js
@@ -0,0 +1,111 @@
+'use strict';
+
+/* eslint-disable no-console */
+
+const BbPromise = require('bluebird');
+const _ = require('lodash');
+const fetch = require('node-fetch');
+const configUtils = require('./config');
+const isTrackingDisabled = require('./isTrackingDisabled');
+const isValidEventName = require('./userStatsValidation');
+
+const TRACKING_IS_DISABLED = isTrackingDisabled();
+const TRACK_URL = 'https://serverless.com/api/framework/track';
+const IDENTIFY_URL = 'https://serverless.com/api/framework/identify';
+const DEBUG = false;
+
+function debug() {
+ if (DEBUG) console.log(arguments);
+}
+
+/* note tracking swallows errors */
+function request(url, payload) {
+ return fetch(url, {
+ method: 'POST',
+ // set to 1000 b/c no response needed
+ timeout: '1000',
+ body: JSON.stringify(payload),
+ })
+ .then((response) => {
+ if (response.status === 404) {
+ return BbPromise.reject('404 api not found');
+ }
+ return response.json();
+ })
+ .then((res) => BbPromise.resolve(res))
+ .catch((e) => BbPromise.resolve(e));
+}
+
+function track(eventName, payload) {
+ const data = payload || {};
+ let userId = data.id;
+ let userEmail = data.email;
+
+ // exit early if tracking disabled
+ if (TRACKING_IS_DISABLED && !data.force) {
+ debug('abort .track call TRACKING_IS_DISABLED');
+ return BbPromise.resolve();
+ }
+
+ // getConfig for values if not provided from .track call
+ if (!userId || !userEmail) {
+ const config = configUtils.getConfig();
+ userId = config.userId;
+ if (config.users && config.users[userId] && config.users[userId].email) {
+ userEmail = config.users[userId].email;
+ } else {
+ debug('exit early if no user data or email found');
+ return BbPromise.resolve();
+ }
+ }
+
+ // automatically add `framework:` prefix
+ if (eventName.indexOf('framework:') === -1) {
+ eventName = `framework:${eventName}`; // eslint-disable-line
+ }
+
+ // to ensure clean data, validate event name
+ if (!isValidEventName(eventName)) {
+ return BbPromise.resolve();
+ }
+
+ const defaultData = {
+ event: eventName,
+ id: userId,
+ email: userEmail,
+ data: {
+ id: userId,
+ timestamp: Math.round(+new Date() / 1000),
+ },
+ };
+
+ delete data.force;
+ const eventData = _.merge(defaultData, data);
+ if (DEBUG) {
+ debug('.track call', eventData);
+ return BbPromise.resolve();
+ }
+ return request(TRACK_URL, eventData);
+}
+
+function identify(payload) {
+ const data = payload || {};
+ if (TRACKING_IS_DISABLED && !data.force) {
+ if (DEBUG) {
+ console.log('abort .identify call');
+ }
+ // exit early is tracking disabled
+ return BbPromise.resolve();
+ }
+ delete data.force;
+ if (DEBUG) {
+ console.log('.identify call', data);
+ return BbPromise.resolve();
+ }
+ return request(IDENTIFY_URL, data);
+}
+
+module.exports = {
+ track,
+ identify,
+};
diff --git a/lib/utils/userStatsValidation.js b/lib/utils/userStatsValidation.js
new file mode 100644
index 000000000..16693b5bc
--- /dev/null
+++ b/lib/utils/userStatsValidation.js
@@ -0,0 +1,85 @@
+'use strict';
+
+/* eslint-disable no-console */
+
+/**
+ * Validate Event names to keep data clean
+ */
+const chalk = require('chalk');
+
+const VALID_TRACKING_PROJECTS = ['framework'];
+const VALID_TRACKING_OBJECTS = ['user', 'service'];
+
+function containsSeparators(eventName) {
+ const underscores = (eventName.match(/_/g) || []).length;
+ const colons = (eventName.match(/:/g) || []).length;
+ if (underscores !== 1) {
+ console.log(chalk.red('Tracking Error:'));
+ console.log(
+ chalk.red(`Event name must have single underscore. "${eventName}" contains ${underscores}`));
+ return false;
+ }
+ if (colons !== 1) {
+ console.log(chalk.red('Tracking Error:'));
+ console.log(chalk.red(`Event name must have single colon. "${eventName}" contains ${colons}`));
+ return false;
+ }
+ return true;
+}
+
+// Validate tracking project for clean events
+function isValidProject(project) {
+ const isValid = VALID_TRACKING_PROJECTS.indexOf(project) !== -1;
+ if (!isValid) {
+ console.log(chalk.red('Tracking Error:'));
+ console.log(`"${project}" is invalid project. Must be one of`, VALID_TRACKING_PROJECTS);
+ }
+ return isValid;
+}
+
+// Validate tracking objects for clean events
+function isValidObject(key) {
+ const isValid = VALID_TRACKING_OBJECTS.indexOf(key) !== -1;
+ if (!isValid) {
+ console.log(chalk.red('Tracking Error:'));
+ console.log(`"${key}" is invalid tracking object. Must be one of`, VALID_TRACKING_OBJECTS);
+ }
+ return isValid;
+}
+
+function formattingWarning(eventName) {
+ console.log(chalk.red(`Incorrect tracking event format: "${eventName}"`));
+ console.log(`Tracking event must match ${chalk.yellow('product:objectName_actionName')}`);
+ console.log(`It must be all camelCase: ${chalk.yellow('camelCase:camelCase_camelCase')}`);
+ console.log(`Here is an Example ${chalk.green('framework:user_loggedIn')}`);
+ console.log('Note: "framework:" is automatically prepended');
+ console.log(`Usage: ${chalk.yellow('track("user_loggedIn", { ..extraData });')}`);
+ console.log('-----------------------------');
+ return false;
+}
+
+// validate events to naming conventions. clean data FTW!
+module.exports = function isValidEventName(eventName) {
+ // match framework:objectName_actionName
+ const matches = eventName.match(/([a-zA-Z]*):([a-zA-Z]*)_(.*)/);
+ if (!containsSeparators(eventName) || !matches) {
+ return formattingWarning(eventName);
+ }
+ const project = matches[1];
+ const object = matches[2];
+ const action = matches[3];
+
+ // if missing any parts of event, exit;
+ if (!project || !object || !action) {
+ return formattingWarning(eventName);
+ }
+ // validate project name
+ if (!isValidProject(project)) {
+ return formattingWarning(eventName);
+ }
+ // validate object name
+ if (!isValidObject(object)) {
+ return formattingWarning(eventName);
+ }
+ return true;
+};
diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
index 335816aac..dce12a188 100644
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -27,9 +27,9 @@
"dev": true,
"dependencies": {
"acorn": {
- "version": "4.0.11",
+ "version": "4.0.13",
"from": "acorn@>=4.0.4 <5.0.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.11.tgz",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
"dev": true
}
}
@@ -97,9 +97,9 @@
"dev": true
},
"ansi-regex": {
- "version": "2.0.0",
+ "version": "2.1.1",
"from": "ansi-regex@>=2.0.0 <3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz"
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz"
},
"ansi-styles": {
"version": "2.2.1",
@@ -125,14 +125,14 @@
"dev": true
},
"archiver": {
- "version": "1.2.0",
+ "version": "1.3.0",
"from": "archiver@>=1.1.0 <2.0.0",
- "resolved": "https://registry.npmjs.org/archiver/-/archiver-1.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/archiver/-/archiver-1.3.0.tgz",
"dependencies": {
"async": {
- "version": "2.1.4",
+ "version": "2.4.1",
"from": "async@>=2.0.0 <3.0.0",
- "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz"
+ "resolved": "https://registry.npmjs.org/async/-/async-2.4.1.tgz"
}
}
},
@@ -224,8 +224,7 @@
"asynckit": {
"version": "0.4.0",
"from": "asynckit@>=0.4.0 <0.5.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "dev": true
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz"
},
"autolinker": {
"version": "0.15.3",
@@ -234,14 +233,14 @@
"dev": true
},
"aws-sdk": {
- "version": "2.7.13",
- "from": "aws-sdk@>=2.3.17 <3.0.0",
- "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.7.13.tgz",
+ "version": "2.56.0",
+ "from": "aws-sdk@>=2.7.13 <3.0.0",
+ "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.56.0.tgz",
"dependencies": {
"uuid": {
- "version": "3.0.0",
- "from": "uuid@3.0.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz"
+ "version": "3.0.1",
+ "from": "uuid@3.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz"
}
}
},
@@ -381,21 +380,14 @@
"optional": true
},
"bl": {
- "version": "1.1.2",
+ "version": "1.2.1",
"from": "bl@>=1.0.0 <2.0.0",
- "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz",
- "dependencies": {
- "readable-stream": {
- "version": "2.0.6",
- "from": "readable-stream@>=2.0.5 <2.1.0",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz"
- }
- }
+ "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz"
},
"bluebird": {
- "version": "3.4.6",
+ "version": "3.5.0",
"from": "bluebird@>=3.4.0 <4.0.0",
- "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.6.tgz"
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz"
},
"boom": {
"version": "2.10.1",
@@ -404,9 +396,9 @@
"dev": true
},
"brace-expansion": {
- "version": "1.1.6",
- "from": "brace-expansion@>=1.0.0 <2.0.0",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz"
+ "version": "1.1.7",
+ "from": "brace-expansion@>=1.1.7 <2.0.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz"
},
"braces": {
"version": "1.8.5",
@@ -441,9 +433,9 @@
"dev": true
},
"buffer": {
- "version": "4.9.1",
- "from": "buffer@4.9.1",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz"
+ "version": "5.0.6",
+ "from": "buffer@5.0.6",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.6.tgz"
},
"buffer-crc32": {
"version": "0.2.13",
@@ -452,7 +444,7 @@
},
"buffer-shims": {
"version": "1.0.0",
- "from": "buffer-shims@>=1.0.0 <2.0.0",
+ "from": "buffer-shims@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz"
},
"builtin-modules": {
@@ -521,11 +513,23 @@
"resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz",
"dev": true
},
+ "chai-as-promised": {
+ "version": "6.0.0",
+ "from": "chai-as-promised@>=6.0.0 <7.0.0",
+ "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-6.0.0.tgz",
+ "dev": true
+ },
"chalk": {
"version": "1.1.3",
"from": "chalk@>=1.1.1 <2.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz"
},
+ "check-error": {
+ "version": "1.0.2",
+ "from": "check-error@>=1.0.2 <2.0.0",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+ "dev": true
+ },
"ci-info": {
"version": "1.0.0",
"from": "ci-info@>=1.0.0 <2.0.0",
@@ -613,13 +617,13 @@
},
"component-emitter": {
"version": "1.2.1",
- "from": "component-emitter@>=1.2.0 <1.3.0",
+ "from": "component-emitter@>=1.2.0 <2.0.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz"
},
"compress-commons": {
- "version": "1.1.0",
+ "version": "1.2.0",
"from": "compress-commons@>=1.1.0 <2.0.0",
- "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-1.1.0.tgz"
+ "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-1.2.0.tgz"
},
"concat-map": {
"version": "0.0.1",
@@ -651,9 +655,9 @@
"dev": true
},
"cookiejar": {
- "version": "2.0.6",
- "from": "cookiejar@2.0.6",
- "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.0.6.tgz"
+ "version": "2.1.1",
+ "from": "cookiejar@>=2.0.6 <3.0.0",
+ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.1.tgz"
},
"core-js": {
"version": "2.4.1",
@@ -672,6 +676,12 @@
"resolved": "https://registry.npmjs.org/coveralls/-/coveralls-2.13.1.tgz",
"dev": true,
"dependencies": {
+ "esprima": {
+ "version": "2.7.3",
+ "from": "esprima@>=2.6.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+ "dev": true
+ },
"js-yaml": {
"version": "3.6.1",
"from": "js-yaml@3.6.1",
@@ -680,10 +690,15 @@
}
}
},
+ "crc": {
+ "version": "3.4.4",
+ "from": "crc@>=3.4.4 <4.0.0",
+ "resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz"
+ },
"crc32-stream": {
- "version": "1.0.0",
- "from": "crc32-stream@>=1.0.0 <2.0.0",
- "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-1.0.0.tgz"
+ "version": "2.0.0",
+ "from": "crc32-stream@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-2.0.0.tgz"
},
"create-error-class": {
"version": "3.0.2",
@@ -740,9 +755,9 @@
}
},
"debug": {
- "version": "2.3.3",
+ "version": "2.6.8",
"from": "debug@>=2.0.0 <3.0.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz"
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz"
},
"decamelize": {
"version": "1.2.0",
@@ -751,9 +766,9 @@
"dev": true
},
"decompress": {
- "version": "4.0.0",
+ "version": "4.2.0",
"from": "decompress@>=4.0.0 <5.0.0",
- "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.0.0.tgz"
+ "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.0.tgz"
},
"decompress-tar": {
"version": "4.1.0",
@@ -766,14 +781,28 @@
"resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.0.tgz"
},
"decompress-targz": {
- "version": "4.0.0",
+ "version": "4.1.0",
"from": "decompress-targz@>=4.0.0 <5.0.0",
- "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.0.0.tgz"
+ "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.0.tgz",
+ "dependencies": {
+ "file-type": {
+ "version": "4.3.0",
+ "from": "file-type@>=4.3.0 <5.0.0",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-4.3.0.tgz"
+ }
+ }
},
"decompress-unzip": {
"version": "4.0.1",
"from": "decompress-unzip@>=4.0.1 <5.0.0",
- "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz"
+ "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz",
+ "dependencies": {
+ "get-stream": {
+ "version": "2.3.1",
+ "from": "get-stream@>=2.2.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz"
+ }
+ }
},
"deep-eql": {
"version": "0.1.3",
@@ -790,9 +819,9 @@
}
},
"deep-extend": {
- "version": "0.4.1",
+ "version": "0.4.2",
"from": "deep-extend@>=0.4.0 <0.5.0",
- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz"
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz"
},
"deep-is": {
"version": "0.1.3",
@@ -870,9 +899,9 @@
"dev": true
},
"download": {
- "version": "5.0.2",
+ "version": "5.0.3",
"from": "download@>=5.0.2 <6.0.0",
- "resolved": "https://registry.npmjs.org/download/-/download-5.0.2.tgz"
+ "resolved": "https://registry.npmjs.org/download/-/download-5.0.3.tgz"
},
"duplexer3": {
"version": "0.1.4",
@@ -892,16 +921,9 @@
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz"
},
"end-of-stream": {
- "version": "1.1.0",
+ "version": "1.4.0",
"from": "end-of-stream@>=1.0.0 <2.0.0",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz",
- "dependencies": {
- "once": {
- "version": "1.3.3",
- "from": "once@>=1.3.0 <1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz"
- }
- }
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz"
},
"errno": {
"version": "0.1.4",
@@ -928,9 +950,9 @@
"dev": true
},
"es5-ext": {
- "version": "0.10.18",
+ "version": "0.10.21",
"from": "es5-ext@>=0.10.14 <0.11.0",
- "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.18.tgz",
+ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.21.tgz",
"dev": true
},
"es6-iterator": {
@@ -980,6 +1002,12 @@
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz",
"dev": true,
"dependencies": {
+ "esprima": {
+ "version": "2.7.3",
+ "from": "esprima@>=2.7.1 <3.0.0",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+ "dev": true
+ },
"estraverse": {
"version": "1.9.3",
"from": "estraverse@>=1.9.1 <2.0.0",
@@ -1005,12 +1033,6 @@
"from": "shelljs@>=0.7.5 <0.8.0",
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.7.tgz",
"dev": true
- },
- "strip-json-comments": {
- "version": "2.0.1",
- "from": "strip-json-comments@>=2.0.1 <2.1.0",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "dev": true
}
}
},
@@ -1073,9 +1095,9 @@
"dev": true
},
"esprima": {
- "version": "2.7.3",
- "from": "esprima@>=2.6.0 <3.0.0",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz"
+ "version": "3.1.3",
+ "from": "esprima@>=3.1.1 <4.0.0",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz"
},
"esquery": {
"version": "1.0.0",
@@ -1140,9 +1162,9 @@
"dev": true
},
"extend": {
- "version": "3.0.0",
+ "version": "3.0.1",
"from": "extend@>=3.0.0 <4.0.0",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz"
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz"
},
"extend-shallow": {
"version": "2.0.1",
@@ -1203,14 +1225,14 @@
"dev": true
},
"filename-reserved-regex": {
- "version": "1.0.0",
- "from": "filename-reserved-regex@>=1.0.0 <2.0.0",
- "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz"
+ "version": "2.0.0",
+ "from": "filename-reserved-regex@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz"
},
"filenamify": {
- "version": "1.2.1",
- "from": "filenamify@>=1.2.1 <2.0.0",
- "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-1.2.1.tgz"
+ "version": "2.0.0",
+ "from": "filenamify@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-2.0.0.tgz"
},
"fileset": {
"version": "2.0.3",
@@ -1219,9 +1241,9 @@
"dev": true
},
"filesize": {
- "version": "3.3.0",
+ "version": "3.5.10",
"from": "filesize@>=3.3.0 <4.0.0",
- "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.3.0.tgz"
+ "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.5.10.tgz"
},
"fill-keys": {
"version": "1.0.2",
@@ -1272,9 +1294,9 @@
"dev": true
},
"form-data": {
- "version": "1.0.0-rc3",
- "from": "form-data@1.0.0-rc3",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc3.tgz"
+ "version": "2.1.4",
+ "from": "form-data@>=2.1.1 <3.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz"
},
"formatio": {
"version": "1.1.1",
@@ -1283,9 +1305,9 @@
"dev": true
},
"formidable": {
- "version": "1.0.17",
- "from": "formidable@>=1.0.14 <1.1.0",
- "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.17.tgz"
+ "version": "1.1.1",
+ "from": "formidable@>=1.1.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.1.1.tgz"
},
"fs-extra": {
"version": "0.26.7",
@@ -1332,9 +1354,9 @@
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz"
},
"get-stream": {
- "version": "2.3.1",
- "from": "get-stream@>=2.2.0 <3.0.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz"
+ "version": "3.0.0",
+ "from": "get-stream@>=3.0.0 <4.0.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz"
},
"getpass": {
"version": "0.1.7",
@@ -1351,9 +1373,9 @@
}
},
"glob": {
- "version": "7.1.1",
+ "version": "7.1.2",
"from": "glob@>=7.0.0 <8.0.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz"
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz"
},
"glob-base": {
"version": "0.3.0",
@@ -1379,9 +1401,9 @@
"resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz"
},
"got": {
- "version": "6.6.3",
+ "version": "6.7.1",
"from": "got@>=6.3.0 <7.0.0",
- "resolved": "https://registry.npmjs.org/got/-/got-6.6.3.tgz"
+ "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz"
},
"graceful-fs": {
"version": "4.1.11",
@@ -1393,25 +1415,16 @@
"from": "graceful-readlink@>=1.0.0",
"resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz"
},
+ "graphlib": {
+ "version": "2.1.1",
+ "from": "graphlib@>=2.1.1 <3.0.0",
+ "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.1.tgz"
+ },
"gray-matter": {
"version": "2.1.1",
"from": "gray-matter@>=2.1.0 <3.0.0",
"resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-2.1.1.tgz",
- "dev": true,
- "dependencies": {
- "esprima": {
- "version": "3.1.3",
- "from": "esprima@>=3.1.1 <4.0.0",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
- "dev": true
- },
- "js-yaml": {
- "version": "3.8.4",
- "from": "js-yaml@>=3.8.1 <4.0.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.4.tgz",
- "dev": true
- }
- }
+ "dev": true
},
"growl": {
"version": "1.9.2",
@@ -1426,9 +1439,9 @@
"dev": true
},
"handlebars": {
- "version": "4.0.8",
+ "version": "4.0.10",
"from": "handlebars@>=4.0.1 <5.0.0",
- "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.8.tgz",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.10.tgz",
"dev": true,
"dependencies": {
"source-map": {
@@ -1524,9 +1537,9 @@
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz"
},
"iconv-lite": {
- "version": "0.4.15",
+ "version": "0.4.17",
"from": "iconv-lite@>=0.4.13 <0.5.0",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz"
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.17.tgz"
},
"ieee754": {
"version": "1.1.8",
@@ -1534,9 +1547,9 @@
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz"
},
"ignore": {
- "version": "3.3.0",
+ "version": "3.3.3",
"from": "ignore@>=3.2.0 <4.0.0",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.0.tgz",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.3.tgz",
"dev": true
},
"immediate": {
@@ -1548,8 +1561,7 @@
"imurmurhash": {
"version": "0.1.4",
"from": "imurmurhash@>=0.1.4 <0.2.0",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "dev": true
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz"
},
"inflight": {
"version": "1.0.6",
@@ -1590,11 +1602,6 @@
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
"dev": true
},
- "is-absolute": {
- "version": "0.1.7",
- "from": "is-absolute@>=0.1.5 <0.2.0",
- "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz"
- },
"is-arrayish": {
"version": "0.2.1",
"from": "is-arrayish@>=0.2.1 <0.3.0",
@@ -1686,9 +1693,9 @@
"dev": true
},
"is-natural-number": {
- "version": "2.1.1",
- "from": "is-natural-number@>=2.0.0 <3.0.0",
- "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-2.1.1.tgz"
+ "version": "4.0.1",
+ "from": "is-natural-number@>=4.0.1 <5.0.0",
+ "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz"
},
"is-number": {
"version": "2.1.0",
@@ -1749,11 +1756,6 @@
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
"dev": true
},
- "is-relative": {
- "version": "0.1.3",
- "from": "is-relative@>=0.1.0 <0.2.0",
- "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz"
- },
"is-resolvable": {
"version": "1.0.0",
"from": "is-resolvable@>=1.0.0 <2.0.0",
@@ -1788,6 +1790,11 @@
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
"dev": true
},
+ "is-wsl": {
+ "version": "1.1.0",
+ "from": "is-wsl@>=1.1.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz"
+ },
"isarray": {
"version": "1.0.0",
"from": "isarray@>=1.0.0 <1.1.0",
@@ -1817,6 +1824,12 @@
"resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz",
"dev": true,
"dependencies": {
+ "esprima": {
+ "version": "2.7.3",
+ "from": "esprima@>=2.7.0 <2.8.0",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+ "dev": true
+ },
"glob": {
"version": "5.0.15",
"from": "glob@>=5.0.15 <6.0.0",
@@ -1844,9 +1857,9 @@
"dev": true,
"dependencies": {
"async": {
- "version": "2.4.0",
+ "version": "2.4.1",
"from": "async@>=2.1.4 <3.0.0",
- "resolved": "https://registry.npmjs.org/async/-/async-2.4.0.tgz",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.4.1.tgz",
"dev": true
}
}
@@ -1877,7 +1890,7 @@
"dependencies": {
"supports-color": {
"version": "3.2.3",
- "from": "supports-color@^3.1.2",
+ "from": "supports-color@>=3.1.2 <4.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
"dev": true
}
@@ -1889,24 +1902,6 @@
"resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.0.tgz",
"dev": true,
"dependencies": {
- "debug": {
- "version": "2.6.6",
- "from": "debug@>=2.6.3 <3.0.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.6.tgz",
- "dev": true
- },
- "ms": {
- "version": "0.7.3",
- "from": "ms@0.7.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz",
- "dev": true
- },
- "rimraf": {
- "version": "2.6.1",
- "from": "rimraf@>=2.6.1 <3.0.0",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz",
- "dev": true
- },
"source-map": {
"version": "0.5.6",
"from": "source-map@>=0.5.3 <0.6.0",
@@ -2088,9 +2083,9 @@
"dev": true
},
"js-yaml": {
- "version": "3.7.0",
+ "version": "3.8.4",
"from": "js-yaml@>=3.6.1 <4.0.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz"
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.4.tgz"
},
"jsbn": {
"version": "0.1.1",
@@ -2106,15 +2101,9 @@
"dev": true,
"dependencies": {
"acorn": {
- "version": "4.0.11",
+ "version": "4.0.13",
"from": "acorn@>=4.0.4 <5.0.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.11.tgz",
- "dev": true
- },
- "sax": {
- "version": "1.2.2",
- "from": "sax@>=1.2.1 <2.0.0",
- "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.2.tgz",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
"dev": true
}
}
@@ -2126,9 +2115,9 @@
"dev": true
},
"json-refs": {
- "version": "2.1.6",
+ "version": "2.1.7",
"from": "json-refs@>=2.1.5 <3.0.0",
- "resolved": "https://registry.npmjs.org/json-refs/-/json-refs-2.1.6.tgz",
+ "resolved": "https://registry.npmjs.org/json-refs/-/json-refs-2.1.7.tgz",
"dependencies": {
"commander": {
"version": "2.9.0",
@@ -2221,13 +2210,24 @@
"from": "readable-stream@>=2.0.6 <2.1.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
"dev": true
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "from": "string_decoder@>=0.10.0 <0.11.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "dev": true
}
}
},
+ "jwt-decode": {
+ "version": "2.2.0",
+ "from": "jwt-decode@>=2.2.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz"
+ },
"kind-of": {
- "version": "3.2.0",
+ "version": "3.2.2",
"from": "kind-of@>=3.0.2 <4.0.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"dev": true
},
"klaw": {
@@ -2306,9 +2306,9 @@
}
},
"lodash": {
- "version": "4.17.2",
+ "version": "4.17.4",
"from": "lodash@>=4.13.1 <5.0.0",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.2.tgz"
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz"
},
"lodash._arraycopy": {
"version": "3.0.0",
@@ -2459,6 +2459,11 @@
"from": "lowercase-keys@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz"
},
+ "make-dir": {
+ "version": "1.0.0",
+ "from": "make-dir@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.0.0.tgz"
+ },
"makeerror": {
"version": "1.0.11",
"from": "makeerror@>=1.0.0 <1.1.0",
@@ -2479,7 +2484,7 @@
"dependencies": {
"commander": {
"version": "2.9.0",
- "from": "commander@^2.9.0",
+ "from": "commander@>=2.9.0 <3.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
"dev": true
},
@@ -2537,7 +2542,7 @@
},
"methods": {
"version": "1.1.2",
- "from": "methods@>=1.1.1 <1.2.0",
+ "from": "methods@>=1.1.1 <2.0.0",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz"
},
"micromatch": {
@@ -2547,24 +2552,24 @@
"dev": true
},
"mime": {
- "version": "1.3.4",
- "from": "mime@1.3.4",
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz"
+ "version": "1.3.6",
+ "from": "mime@>=1.3.4 <2.0.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz"
},
"mime-db": {
- "version": "1.25.0",
- "from": "mime-db@>=1.25.0 <1.26.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.25.0.tgz"
+ "version": "1.27.0",
+ "from": "mime-db@>=1.27.0 <1.28.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz"
},
"mime-types": {
- "version": "2.1.13",
- "from": "mime-types@>=2.1.3 <3.0.0",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.13.tgz"
+ "version": "2.1.15",
+ "from": "mime-types@>=2.1.12 <3.0.0",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz"
},
"minimatch": {
- "version": "3.0.3",
- "from": "minimatch@>=3.0.2 <4.0.0",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz"
+ "version": "3.0.4",
+ "from": "minimatch@>=3.0.4 <4.0.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz"
},
"minimist": {
"version": "1.2.0",
@@ -2607,6 +2612,18 @@
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.0.tgz",
"dev": true
},
+ "glob": {
+ "version": "7.1.1",
+ "from": "glob@7.1.1",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz",
+ "dev": true
+ },
+ "ms": {
+ "version": "0.7.2",
+ "from": "ms@0.7.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
+ "dev": true
+ },
"supports-color": {
"version": "3.1.2",
"from": "supports-color@3.1.2",
@@ -2634,14 +2651,14 @@
"dev": true
},
"moment": {
- "version": "2.17.0",
+ "version": "2.18.1",
"from": "moment@>=2.13.0 <3.0.0",
- "resolved": "https://registry.npmjs.org/moment/-/moment-2.17.0.tgz"
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz"
},
"ms": {
- "version": "0.7.2",
- "from": "ms@0.7.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz"
+ "version": "2.0.0",
+ "from": "ms@2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
},
"mute-stream": {
"version": "0.0.5",
@@ -2667,9 +2684,9 @@
"dev": true
},
"node-fetch": {
- "version": "1.6.3",
- "from": "node-fetch@>=1.5.3 <2.0.0",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.6.3.tgz"
+ "version": "1.7.0",
+ "from": "node-fetch@>=1.6.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.0.tgz"
},
"node-int64": {
"version": "0.4.0",
@@ -2683,11 +2700,6 @@
"resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-4.6.1.tgz",
"dev": true
},
- "node-status-codes": {
- "version": "2.0.1",
- "from": "node-status-codes@>=2.0.0 <3.0.0",
- "resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-2.0.1.tgz"
- },
"nopt": {
"version": "3.0.6",
"from": "nopt@>=3.0.0 <4.0.0",
@@ -2701,9 +2713,9 @@
"dev": true
},
"normalize-path": {
- "version": "2.0.1",
+ "version": "2.1.1",
"from": "normalize-path@>=2.0.0 <3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.0.1.tgz"
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz"
},
"number-is-nan": {
"version": "1.0.1",
@@ -2712,9 +2724,9 @@
"dev": true
},
"nwmatcher": {
- "version": "1.3.9",
+ "version": "1.4.0",
"from": "nwmatcher@>=1.3.9 <2.0.0",
- "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.3.9.tgz",
+ "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.0.tgz",
"dev": true
},
"oauth-sign": {
@@ -2724,9 +2736,9 @@
"dev": true
},
"object-assign": {
- "version": "4.1.0",
+ "version": "4.1.1",
"from": "object-assign@>=4.0.1 <5.0.0",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz"
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
},
"object-keys": {
"version": "1.0.11",
@@ -2763,6 +2775,11 @@
"resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
"dev": true
},
+ "opn": {
+ "version": "5.0.0",
+ "from": "opn@>=5.0.0 <6.0.0",
+ "resolved": "https://registry.npmjs.org/opn/-/opn-5.0.0.tgz"
+ },
"optimist": {
"version": "0.6.1",
"from": "optimist@>=0.6.1 <0.7.0",
@@ -2861,9 +2878,9 @@
"dev": true
},
"path-loader": {
- "version": "1.0.1",
- "from": "path-loader@>=1.0.1 <2.0.0",
- "resolved": "https://registry.npmjs.org/path-loader/-/path-loader-1.0.1.tgz"
+ "version": "1.0.2",
+ "from": "path-loader@>=1.0.2 <2.0.0",
+ "resolved": "https://registry.npmjs.org/path-loader/-/path-loader-1.0.2.tgz"
},
"path-parse": {
"version": "1.0.5",
@@ -2987,9 +3004,9 @@
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz"
},
"qs": {
- "version": "2.3.3",
- "from": "qs@2.3.3",
- "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz"
+ "version": "6.4.0",
+ "from": "qs@>=6.1.0 <7.0.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz"
},
"querystring": {
"version": "0.2.0",
@@ -3003,9 +3020,9 @@
"dev": true
},
"rc": {
- "version": "1.1.6",
- "from": "rc@>=1.1.2 <2.0.0",
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.6.tgz"
+ "version": "1.2.1",
+ "from": "rc@>=1.1.6 <2.0.0",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz"
},
"read-pkg": {
"version": "1.1.0",
@@ -3020,9 +3037,9 @@
"dev": true
},
"readable-stream": {
- "version": "2.2.2",
+ "version": "2.2.9",
"from": "readable-stream@>=2.0.0 <3.0.0",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.2.tgz"
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz"
},
"readline2": {
"version": "1.0.1",
@@ -3050,11 +3067,6 @@
}
}
},
- "reduce-component": {
- "version": "1.0.1",
- "from": "reduce-component@1.0.1",
- "resolved": "https://registry.npmjs.org/reduce-component/-/reduce-component-1.0.1.tgz"
- },
"regenerator-runtime": {
"version": "0.10.5",
"from": "regenerator-runtime@>=0.10.0 <0.11.0",
@@ -3081,6 +3093,11 @@
}
}
},
+ "remove-trailing-separator": {
+ "version": "1.0.1",
+ "from": "remove-trailing-separator@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz"
+ },
"repeat-element": {
"version": "1.1.2",
"from": "repeat-element@>=1.1.2 <2.0.0",
@@ -3110,12 +3127,6 @@
"resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz",
"dev": true,
"dependencies": {
- "form-data": {
- "version": "2.1.4",
- "from": "form-data@>=2.1.1 <2.2.0",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz",
- "dev": true
- },
"qs": {
"version": "6.3.2",
"from": "qs@>=6.3.0 <6.4.0",
@@ -3181,9 +3192,9 @@
"optional": true
},
"rimraf": {
- "version": "2.5.4",
+ "version": "2.6.1",
"from": "rimraf@>=2.2.8 <3.0.0",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz"
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz"
},
"run-async": {
"version": "0.1.0",
@@ -3197,6 +3208,11 @@
"resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz",
"dev": true
},
+ "safe-buffer": {
+ "version": "5.0.1",
+ "from": "safe-buffer@>=5.0.1 <6.0.0",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz"
+ },
"samsam": {
"version": "1.1.2",
"from": "samsam@1.1.2",
@@ -3210,9 +3226,9 @@
"dev": true
},
"sax": {
- "version": "1.1.5",
- "from": "sax@1.1.5",
- "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.5.tgz"
+ "version": "1.2.1",
+ "from": "sax@1.2.1",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz"
},
"seek-bzip": {
"version": "1.0.5",
@@ -3264,6 +3280,12 @@
"resolved": "https://registry.npmjs.org/sinon-bluebird/-/sinon-bluebird-3.1.0.tgz",
"dev": true
},
+ "sinon-chai": {
+ "version": "2.10.0",
+ "from": "sinon-chai@>=2.9.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-2.10.0.tgz",
+ "dev": true
+ },
"slash": {
"version": "1.0.0",
"from": "slash@>=1.0.0 <2.0.0",
@@ -3275,6 +3297,11 @@
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz",
"dev": true
},
+ "slide": {
+ "version": "1.1.6",
+ "from": "slide@>=1.1.5 <2.0.0",
+ "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz"
+ },
"sntp": {
"version": "1.0.9",
"from": "sntp@>=1.0.0 <2.0.0",
@@ -3346,9 +3373,9 @@
"dev": true
},
"string_decoder": {
- "version": "0.10.31",
- "from": "string_decoder@>=0.10.0 <0.11.0",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+ "version": "1.0.1",
+ "from": "string_decoder@>=1.0.0 <1.1.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz"
},
"string-width": {
"version": "1.0.2",
@@ -3386,48 +3413,24 @@
"dev": true
},
"strip-dirs": {
- "version": "1.1.1",
- "from": "strip-dirs@>=1.1.1 <2.0.0",
- "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz",
- "dependencies": {
- "get-stdin": {
- "version": "4.0.1",
- "from": "get-stdin@>=4.0.1 <5.0.0",
- "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz"
- }
- }
+ "version": "2.0.0",
+ "from": "strip-dirs@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.0.0.tgz"
},
"strip-json-comments": {
- "version": "1.0.4",
- "from": "strip-json-comments@>=1.0.4 <1.1.0",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz"
+ "version": "2.0.1",
+ "from": "strip-json-comments@>=2.0.1 <2.1.0",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz"
},
"strip-outer": {
"version": "1.0.0",
"from": "strip-outer@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.0.tgz"
},
- "sum-up": {
- "version": "1.0.3",
- "from": "sum-up@>=1.0.1 <2.0.0",
- "resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.3.tgz"
- },
"superagent": {
- "version": "1.8.4",
- "from": "superagent@>=1.6.1 <2.0.0",
- "resolved": "https://registry.npmjs.org/superagent/-/superagent-1.8.4.tgz",
- "dependencies": {
- "isarray": {
- "version": "0.0.1",
- "from": "isarray@0.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
- },
- "readable-stream": {
- "version": "1.0.27-1",
- "from": "readable-stream@1.0.27-1",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.27-1.tgz"
- }
- }
+ "version": "3.5.2",
+ "from": "superagent@>=3.5.2 <4.0.0",
+ "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.5.2.tgz"
},
"supports-color": {
"version": "2.0.0",
@@ -3467,9 +3470,9 @@
}
},
"tar-stream": {
- "version": "1.5.2",
+ "version": "1.5.4",
"from": "tar-stream@>=1.5.0 <2.0.0",
- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.2.tgz"
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.4.tgz"
},
"test-exclude": {
"version": "3.3.0",
@@ -3487,15 +3490,7 @@
"version": "2.2.0",
"from": "then-request@>=2.0.1 <3.0.0",
"resolved": "https://registry.npmjs.org/then-request/-/then-request-2.2.0.tgz",
- "dev": true,
- "dependencies": {
- "qs": {
- "version": "6.4.0",
- "from": "qs@>=6.1.0 <7.0.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
- "dev": true
- }
- }
+ "dev": true
},
"throat": {
"version": "3.0.0",
@@ -3509,9 +3504,9 @@
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
},
"timed-out": {
- "version": "3.0.0",
- "from": "timed-out@>=3.0.0 <4.0.0",
- "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-3.0.0.tgz"
+ "version": "4.0.1",
+ "from": "timed-out@>=4.0.0 <5.0.0",
+ "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz"
},
"tmpl": {
"version": "1.0.4",
@@ -3605,9 +3600,9 @@
"dev": true
},
"uglify-js": {
- "version": "2.8.26",
+ "version": "2.8.27",
"from": "uglify-js@>=2.6.0 <3.0.0",
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.26.tgz",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.27.tgz",
"dev": true,
"optional": true,
"dependencies": {
@@ -3628,9 +3623,9 @@
"optional": true
},
"unbzip2-stream": {
- "version": "1.0.10",
+ "version": "1.2.4",
"from": "unbzip2-stream@>=1.0.9 <2.0.0",
- "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.0.10.tgz",
+ "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.2.4.tgz",
"dependencies": {
"base64-js": {
"version": "0.0.8",
@@ -3662,9 +3657,16 @@
"resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz"
},
"uri-js": {
- "version": "2.1.1",
- "from": "uri-js@>=2.1.1 <3.0.0",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-2.1.1.tgz"
+ "version": "3.0.2",
+ "from": "uri-js@>=3.0.2 <4.0.0",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz",
+ "dependencies": {
+ "punycode": {
+ "version": "2.1.0",
+ "from": "punycode@>=2.1.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz"
+ }
+ }
},
"url": {
"version": "0.10.3",
@@ -3718,6 +3720,11 @@
"resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz",
"dev": true
},
+ "walkdir": {
+ "version": "0.0.11",
+ "from": "walkdir@>=0.0.11 <0.0.12",
+ "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.11.tgz"
+ },
"walker": {
"version": "1.0.7",
"from": "walker@>=1.0.5 <1.1.0",
@@ -3812,6 +3819,11 @@
"resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
"dev": true
},
+ "write-file-atomic": {
+ "version": "2.1.0",
+ "from": "write-file-atomic@>=2.1.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.1.0.tgz"
+ },
"xml-name-validator": {
"version": "2.0.1",
"from": "xml-name-validator@>=2.0.1 <3.0.0",
@@ -3819,21 +3831,14 @@
"dev": true
},
"xml2js": {
- "version": "0.4.15",
- "from": "xml2js@0.4.15",
- "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.15.tgz"
+ "version": "0.4.17",
+ "from": "xml2js@0.4.17",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz"
},
"xmlbuilder": {
- "version": "2.6.2",
- "from": "xmlbuilder@2.6.2",
- "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.6.2.tgz",
- "dependencies": {
- "lodash": {
- "version": "3.5.0",
- "from": "lodash@>=3.5.0 <3.6.0",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.5.0.tgz"
- }
- }
+ "version": "4.2.1",
+ "from": "xmlbuilder@4.2.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz"
},
"xtend": {
"version": "4.0.1",
@@ -3868,14 +3873,14 @@
}
},
"yauzl": {
- "version": "2.7.0",
+ "version": "2.8.0",
"from": "yauzl@>=2.4.2 <3.0.0",
- "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.7.0.tgz"
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.8.0.tgz"
},
"zip-stream": {
- "version": "1.1.0",
+ "version": "1.1.1",
"from": "zip-stream@>=1.1.0 <2.0.0",
- "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-1.1.0.tgz"
+ "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-1.1.1.tgz"
}
}
}
diff --git a/package.json b/package.json
index 6fa119cd7..52f4476ac 100644
--- a/package.json
+++ b/package.json
@@ -98,15 +98,19 @@
"https-proxy-agent": "^1.0.0",
"js-yaml": "^3.6.1",
"json-refs": "^2.1.5",
+ "jwt-decode": "^2.2.0",
"lodash": "^4.13.1",
"minimist": "^1.2.0",
"moment": "^2.13.0",
"node-fetch": "^1.6.0",
+ "opn": "^5.0.0",
+ "rc": "^1.1.6",
"replaceall": "^0.1.6",
"resolve-from": "^2.0.0",
"semver": "^5.0.3",
"semver-regex": "^1.0.0",
"shelljs": "^0.6.0",
- "uuid": "^2.0.2"
+ "uuid": "^2.0.2",
+ "write-file-atomic": "^2.1.0"
}
}