From 465fb1ff97755069d42ad137a0c92831562e3f62 Mon Sep 17 00:00:00 2001 From: Sameer Chitley Date: Fri, 16 Feb 2018 00:00:13 -0500 Subject: [PATCH 1/9] Initialized atom plugin. Handles changes, and applies patches --- plugins/atom/.eslintrc.js | 15 + plugins/atom/.gitignore | 3 + plugins/atom/agent | 1 + plugins/atom/atom_dev_setup.sh | 23 + plugins/atom/crdt | 1 + plugins/atom/lib/connectView.js | 58 ++ plugins/atom/lib/messages.js | 176 ++++ plugins/atom/lib/tandem.js | 81 ++ plugins/atom/lib/tandemPlugin.js | 189 ++++ plugins/atom/lib/utils.js | 29 + plugins/atom/package-lock.json | 1520 ++++++++++++++++++++++++++++++ plugins/atom/package.json | 27 + 12 files changed, 2123 insertions(+) create mode 100644 plugins/atom/.eslintrc.js create mode 100644 plugins/atom/.gitignore create mode 120000 plugins/atom/agent create mode 100755 plugins/atom/atom_dev_setup.sh create mode 120000 plugins/atom/crdt create mode 100644 plugins/atom/lib/connectView.js create mode 100644 plugins/atom/lib/messages.js create mode 100644 plugins/atom/lib/tandem.js create mode 100644 plugins/atom/lib/tandemPlugin.js create mode 100644 plugins/atom/lib/utils.js create mode 100644 plugins/atom/package-lock.json create mode 100644 plugins/atom/package.json diff --git a/plugins/atom/.eslintrc.js b/plugins/atom/.eslintrc.js new file mode 100644 index 0000000..14ca81c --- /dev/null +++ b/plugins/atom/.eslintrc.js @@ -0,0 +1,15 @@ +module.exports = { + "extends": "airbnb-base", + "plugins": [ + "import" + ], + "globals": { + "atom": false, + "document": false, + }, + "rules": { + "no-underscore-dangle": "off", + "no-plusplus": "off", + "no-lonely-if": "off" + } +}; diff --git a/plugins/atom/.gitignore b/plugins/atom/.gitignore new file mode 100644 index 0000000..ade14b9 --- /dev/null +++ b/plugins/atom/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +npm-debug.log +node_modules diff --git a/plugins/atom/agent b/plugins/atom/agent new file mode 120000 index 0000000..a765cd6 --- /dev/null +++ b/plugins/atom/agent @@ -0,0 +1 @@ +/Users/sameer/Documents/development/tandem/plugins/atom/../../agent \ No newline at end of file diff --git a/plugins/atom/atom_dev_setup.sh b/plugins/atom/atom_dev_setup.sh new file mode 100755 index 0000000..66a7c7a --- /dev/null +++ b/plugins/atom/atom_dev_setup.sh @@ -0,0 +1,23 @@ +#! /bin/bash + +SCRIPTPATH=$( cd $(dirname $0) ; pwd -P ) + +UNINSTALL_OPTION="--uninstall" + +function install() { + uninstall + + ln -s $SCRIPTPATH/../../agent $SCRIPTPATH + ln -s $SCRIPTPATH/../../crdt $SCRIPTPATH +} + +function uninstall() { + rm -f agent + rm -f crdt +} + +if [ "$1" == "$UNINSTALL_OPTION" ] ; then + uninstall +else + install +fi diff --git a/plugins/atom/crdt b/plugins/atom/crdt new file mode 120000 index 0000000..b766af9 --- /dev/null +++ b/plugins/atom/crdt @@ -0,0 +1 @@ +/Users/sameer/Documents/development/tandem/plugins/atom/../../crdt \ No newline at end of file diff --git a/plugins/atom/lib/connectView.js b/plugins/atom/lib/connectView.js new file mode 100644 index 0000000..2b43165 --- /dev/null +++ b/plugins/atom/lib/connectView.js @@ -0,0 +1,58 @@ +'use babel'; + +import { CompositeDisposable, Disposable } from 'atom'; + +const makeButton = (text, callback) => { + const button = document.createElement('button'); + button.classList.add('btn'); + button.textContent = text; + button.addEventListener('click', callback); + return button; +}; + +export default class ConnectView { + constructor(connectCallback, closeCallback) { + this._element = document.createElement('div'); + this._element.classList.add('tandem-connect-view'); + + const label = document.createElement('div'); + const labelText = document.createElement('span'); + labelText.textContent = 'Enter an IP and port (e.g. localhost 12345)'; + label.appendChild(labelText); + this._element.appendChild(label); + + const input = document.createElement('input'); + input.classList.add('input-text'); + this._element.appendChild(input); + + const okButtonCallback = () => { + connectCallback(input.value); + input.value = ''; + }; + const okButton = makeButton('Connect', okButtonCallback); + + const cancelButtonCallback = () => { + closeCallback(); + input.value = ''; + }; + const cancelButton = makeButton('Cancel', cancelButtonCallback); + + this._element.appendChild(okButton); + this._element.appendChild(cancelButton); + + this._subscriptions = new CompositeDisposable(); + this._subscriptions.add(new Disposable(() => { + okButton.removeEventListener('click', okButtonCallback); + cancelButton.removeEventListener('click', cancelButtonCallback); + })); + } + + destroy() { + this.subscriptions.dispose(); + this._element.remove(); + } + + getElement() { + return this._element; + } +} diff --git a/plugins/atom/lib/messages.js b/plugins/atom/lib/messages.js new file mode 100644 index 0000000..2fedb95 --- /dev/null +++ b/plugins/atom/lib/messages.js @@ -0,0 +1,176 @@ +'use babel'; + +const EditorProtocolMessageType = Object.freeze({ + ApplyText: 'apply-text', + ApplyPatches: 'apply-patches', + CheckDocumentSync: 'check-document-sync', + ConnectTo: 'connect-to', + WriteRequest: 'write-request', + WriteRequestAck: 'write-request-ack', + NewPatches: 'new-patches', + UserChangedEditorText: 'user-changed-editor-text', +}); + +class ConnectTo { + constructor(host, port) { + this._type = EditorProtocolMessageType.ConnectTo; + this._host = host; + this._port = port; + } + + getType() { + return this._type; + } + + toPayload() { + return { + host: this._host, + port: this._port, + }; + } + + static fromPayload(payload) { + return new ConnectTo(payload.host, payload.port); + } +} + +class NewPatches { + constructor(patchList) { + this._type = EditorProtocolMessageType.NewPatches; + this._patchList = patchList; + } + + getType() { + return this._type; + } + + toPayload() { + return { + patch_list: this._patchList, + }; + } + + static fromPayload(payload) { + return new NewPatches(payload.patchList); + } +} + +class ApplyPatches { + constructor(patchList) { + this._type = EditorProtocolMessageType.ApplyPatches; + this._patchList = patchList; + } + + getType() { + return this._type; + } + + getPatchList() { + return this._patchList; + } + + toPayload() { + return { + patch_list: this._patchList, + }; + } + + static fromPayload(payload) { + return new ApplyPatches(payload.patch_list); + } +} + +class WriteRequest { + constructor(seq) { + this._type = EditorProtocolMessageType.WriteRequest; + this._seq = seq; + } + + getType() { + return this._type; + } + + getSeq() { + return this._seq; + } + + toPayload() { + return { + seq: this._seq, + }; + } + + static fromPayload(payload) { + return new WriteRequest(payload.seq); + } +} + +class WriteRequestAck { + constructor(seq) { + this._type = EditorProtocolMessageType.WriteRequestAck; + this._seq = seq; + } + + getType() { + return this._type; + } + + getSeq() { + return this._seq; + } + + toPayload() { + return { + seq: this._seq, + }; + } + + static fromPayload(payload) { + return new WriteRequestAck(payload.seq); + } +} + +const serialize = message => + JSON.stringify({ + type: message.getType(), + payload: message.toPayload(), + version: 1, + }); + +const deserialize = (data) => { + try { + const { type, payload } = JSON.parse(data); + + switch (type) { + case EditorProtocolMessageType.ConnectTo: + return ConnectTo.fromPayload(payload); + + case EditorProtocolMessageType.NewPatches: + return NewPatches.fromPayload(payload); + + case EditorProtocolMessageType.ApplyPatches: + return ApplyPatches.fromPayload(payload); + + case EditorProtocolMessageType.WriteRequest: + return WriteRequest.fromPayload(payload); + + case EditorProtocolMessageType.WriteRequestAck: + return WriteRequestAck.fromPayload(payload); + + default: + throw new Error(); + } + } catch (e) { + throw new Error('Deserialization error.'); + } +}; + +export default { + ConnectTo, + NewPatches, + ApplyPatches, + WriteRequest, + WriteRequestAck, + serialize, + deserialize, +}; diff --git a/plugins/atom/lib/tandem.js b/plugins/atom/lib/tandem.js new file mode 100644 index 0000000..1607b9f --- /dev/null +++ b/plugins/atom/lib/tandem.js @@ -0,0 +1,81 @@ +'use babel'; + +import { CompositeDisposable, Disposable } from 'atom'; +import TandemPlugin from './tandemPlugin'; +import ConnectView from './connectView'; + +export default { + + _tandemAgent: null, + _subscriptions: null, + _connectPanel: null, + + activate() { + this._tandemAgent = new TandemPlugin(); + + const connectView = new ConnectView( + this._connectToHost.bind(this), + () => { this._inputPanel.hide(); }, + ); + this._inputPanel = atom.workspace.addModalPanel({ + item: connectView, + visible: false, + }); + + this._subscriptions = new CompositeDisposable(); + this._subscriptions.add(atom.commands.add('atom-workspace', { + 'tandem:connect': () => this._connect(), + })); + this._subscriptions.add(atom.commands.add('atom-workspace', { + 'tandem:start': () => this._start(), + })); + this._subscriptions.add(atom.commands.add('atom-workspace', { + 'tandem:stop': () => this._stop(), + })); + }, + + deactivate() { + if (this._tandemAgent && this._tandemAgent.isActive()) { + this._tandemAgent.stop(); + } + this._subscriptions.dispose(); + }, + + _getTextEditor() { + return new Promise((res) => { + const editor = atom.workspace.getActiveTextEditor(); + if (editor) { + return res(editor); + } + // Open a new text editor if one is not open + return res(atom.workspace.open()); + }); + }, + + _start() { + this._getTextEditor().then((editor) => { + console.log('starting tandem agent'); + this._tandemAgent.start(editor.getBuffer()); + }); + }, + + _connect() { + this._inputPanel.show(); + }, + + _connectToHost(input) { + this._inputPanel.hide(); + + const args = input.split(' '); + const ip = args[0]; + const port = args[1]; + + this._getTextEditor().then((editor) => { + this._tandemAgent.start(editor.getBuffer(), ip, port); + }); + }, + + _stop() { + this._tandemAgent.stop(); + }, +}; diff --git a/plugins/atom/lib/tandemPlugin.js b/plugins/atom/lib/tandemPlugin.js new file mode 100644 index 0000000..cedd1c3 --- /dev/null +++ b/plugins/atom/lib/tandemPlugin.js @@ -0,0 +1,189 @@ +'use babel'; + +import { CompositeDisposable } from 'atom'; +import { getStringPort, spawnAgent } from './utils'; +import * as m from './messages'; + +const createPatches = event => + [ + { + start: { + row: event.oldRange.start.row, + column: event.oldRange.start.column, + }, + end: { + row: event.oldRange.end.row, + column: event.oldRange.end.column, + }, + text: '', + }, + { + start: { + row: event.oldRange.start.row, + column: event.oldRange.start.column, + }, + end: { + row: 0, + column: 0, + }, + text: event.newText, + }, + ]; + +const eventForBuffer = (buffer) => { + return { + oldRange: { + start: { + row: 0, + column: 0, + }, + end: { + row: 0, + column: 0, + }, + }, + newText: buffer.getText(), + }; +}; + + +export default class TandemPlugin { + constructor() { + this._isActive = false; + } + + _getAgentPort() { + return this._agentPort; + } + + _initialize(buffer) { + this._buffer = buffer; + this._bufferText = buffer.getText(); + this._subscriptions = new CompositeDisposable(); + + this._sendPatches = this._sendPatches.bind(this); + this._subscriptions.add(buffer.onDidChange(this._sendPatches)); + this._processingMessage = false; + } + + isActive() { + return this._isActive; + } + + // TODO: collect changes and process later once didStopChanging? + _sendPatches(event) { + if (this._processingMessage) { + return; + } + + const patches = createPatches(event); + const message = new m.NewPatches(patches); + this._agent.stdin.write(m.serialize(message)); // TODO: encode? + this._agent.stdin.write('\n'); + } + + _startAgent() { + this._agentPort = getStringPort(); + this._agent = spawnAgent([ + '--port', + this._agentPort, + '--log-file', + `/tmp/tandem-agent-${this._agentPort}.log`, + ]); + this._agent.stdin.setEncoding('utf-8'); + + if (this._connectTo) { + const { hostIP, hostPort } = this._connectTo; + const message = new m.ConnectTo(hostIP, hostPort); + this._agent.stdin.write(m.serialize(message)); // TODO: encode? + this.agent.stdin.write('\n'); + } + + const msg = `Bound agent to port ${this._agentPort}`; + console.log(msg); + + + this._agent.stdout.setEncoding('utf-8'); + this._agent.stdout.on('data', this._readAgent.bind(this)); + } + + _readAgent(data) { + // Prevent future 'data' callbacks from being invoked until stream is + // unpaused + this._agent.stdout.pause(); + const message = m.deserialize(data); + + if (!this._processingMessage) { + if (!(message instanceof m.WriteRequest)) { + console.log('Inavlid message. Expected WriteRequest.'); + } else { + // Prevent non-Apply Patches messages from being processed + this._processingMessage = true; + + const ack = new m.WriteRequestAck(message.getSeq()); + this._agent.stdin.write(m.serialize(ack)); + this._agent.stdin.write('\n'); + } + } else { + if (!(message instanceof m.ApplyPatches)) { + console.log('Inavlid message. Expected ApplyPatches'); + } else { + const patches = message.getPatchList(); + for (let i = 0; i < patches.length; i++) { + const { oldStart, oldEnd, newText } = patches[i]; + this._buffer.setTextInRange( + [ + [oldStart.row, oldStart.column], + [oldEnd.row, oldEnd.column], + ], + newText, + ); + } + this._processingMessage = false; + } + } + this._agent.stdout.resume(); + } + + _shutDownAgent() { + this._processingMessage = false; + this._subscriptions.dispose(); + this._agent.kill('SIGINT'); // TODO: fix + } + + start(textBuffer, hostIP, hostPort) { + if (this._isActive) { + const msg = `Cannot start. An instance is already running on :${this._agentPort}`; + console.log(msg); + return; + } + + if (!!hostIP && !hostPort) { + const msg = 'Cannot start. IP specified. You must also provide a port'; + console.log(msg); + return; + } + + this._connectTo = hostIP ? Object.freeze({ ip: hostIP, port: hostPort }) : null; + this._initialize(textBuffer); + + this._startAgent(); + this._isActive = true; + + if (!this._connectTo) { + this._sendPatches(eventForBuffer(textBuffer)); + } + } + + stop() { + if (!this._isActive) { + const msg = 'Cannot stop. No instance running.'; + console.log(msg); + return; + } + + this._shutDownAgent(); + const msg = 'Tandem instance shut down.'; + console.log(msg); + } +} diff --git a/plugins/atom/lib/utils.js b/plugins/atom/lib/utils.js new file mode 100644 index 0000000..79ceb58 --- /dev/null +++ b/plugins/atom/lib/utils.js @@ -0,0 +1,29 @@ +'use babel'; + +import path from 'path'; +import childProcess from 'child_process'; + +const spawnAgent = (args) => { + const dirname = path.resolve(__dirname); + const filename = path.join(dirname, '../agent/main.py'); + + const fileArgs = args || []; + fileArgs.unshift(filename); + + return childProcess.spawn('python3', fileArgs); +}; + +const random = (lo, hi) => { + const min = Math.ceil(lo); + const max = Math.floor(hi); + + return Math.floor((Math.random() * (max - min)) + min); +}; + +const getStringPort = () => + String(random(60600, 62600)); + +export default { + spawnAgent, + getStringPort, +}; diff --git a/plugins/atom/package-lock.json b/plugins/atom/package-lock.json new file mode 100644 index 0000000..59bcc45 --- /dev/null +++ b/plugins/atom/package-lock.json @@ -0,0 +1,1520 @@ +{ + "name": "tandem", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "acorn": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.4.1.tgz", + "integrity": "sha512-XLmq3H/BVvW6/GbxKryGxWORz1ebilSsUDlyC27bXhWGWAZWkGwS6FLHjOlwFXNFoWFQEO/Df4u0YYd0K3BQgQ==", + "dev": true + }, + "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.3.0" + }, + "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.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "ajv-keywords": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "dev": true + }, + "ansi-escapes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz", + "integrity": "sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ==", + "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": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "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.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "atom": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/atom/-/atom-1.1.0.tgz", + "integrity": "sha512-IlkHVhAmp0J9ioEAKRcpjNxylBbsObHlGZYy97yB0NrdDzLjmHWQ6RB2tekRKAxPsnNJLsWA8fgqIrP1H84Xyw==" + }, + "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": { + "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.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "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.1.1" + } + } + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "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 + }, + "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 + }, + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "child_process": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", + "integrity": "sha1-sffn/HPSXn/R1FWtyU4UODAYK1o=" + }, + "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 + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "2.0.0" + } + }, + "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 + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "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 + }, + "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=", + "dev": true + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "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=", + "dev": true + }, + "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.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.6.2" + } + }, + "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" + } + }, + "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" + } + }, + "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=", + "dev": true + }, + "eslint": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.17.0.tgz", + "integrity": "sha512-AyxBUCANU/o/xC0ijGMKavo5Ls3oK6xykiOITlMdjFjrKOsqLrA7Nf5cnrDgcKrHzBirclAZt63XO7YZlVUPwA==", + "dev": true, + "requires": { + "ajv": "5.5.2", + "babel-code-frame": "6.26.0", + "chalk": "2.3.0", + "concat-stream": "1.6.0", + "cross-spawn": "5.1.0", + "debug": "3.1.0", + "doctrine": "2.1.0", + "eslint-scope": "3.7.1", + "eslint-visitor-keys": "1.0.0", + "espree": "3.5.3", + "esquery": "1.0.0", + "esutils": "2.0.2", + "file-entry-cache": "2.0.0", + "functional-red-black-tree": "1.0.1", + "glob": "7.1.2", + "globals": "11.3.0", + "ignore": "3.3.7", + "imurmurhash": "0.1.4", + "inquirer": "3.3.0", + "is-resolvable": "1.1.0", + "js-yaml": "3.10.0", + "json-stable-stringify-without-jsonify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.5", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "pluralize": "7.0.0", + "progress": "2.0.0", + "require-uncached": "1.0.3", + "semver": "5.5.0", + "strip-ansi": "4.0.0", + "strip-json-comments": "2.0.1", + "table": "4.0.2", + "text-table": "0.2.0" + } + }, + "eslint-config-airbnb-base": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-12.1.0.tgz", + "integrity": "sha512-/vjm0Px5ZCpmJqnjIzcFb9TKZrKWz0gnuG/7Gfkt0Db1ELJR51xkZth+t14rYdqWgX836XbuxtArbIHlVhbLBA==", + "dev": true, + "requires": { + "eslint-restricted-globals": "0.1.1" + } + }, + "eslint-config-standard": { + "version": "11.0.0-beta.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-11.0.0-beta.0.tgz", + "integrity": "sha512-f+vs5HAHQo7NRZ3hVe+UVdT5DbebMNaFTWFp95orJ0LUdYPoWdM8xw/bMeO/IZMvHOPmIteGKGc2QOhSXd5nRg==", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", + "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", + "dev": true, + "requires": { + "debug": "2.6.9", + "resolve": "1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.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.9", + "pkg-dir": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "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.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "0.3.2", + "eslint-module-utils": "2.1.1", + "has": "1.0.1", + "lodash.cond": "4.5.2", + "minimatch": "3.0.4", + "read-pkg-up": "2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "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-promise": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.6.0.tgz", + "integrity": "sha512-YQzM6TLTlApAr7Li8vWKR+K3WghjwKcYzY0d2roWap4SLK+kzuagJX/leTetIDWsFcTFnKNJXWupDCD6aZkP2Q==", + "dev": true + }, + "eslint-plugin-standard": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.0.1.tgz", + "integrity": "sha1-NNDJFbRe3G8BA5PH7vOCOwhWXPI=", + "dev": true + }, + "eslint-restricted-globals": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz", + "integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=", + "dev": true + }, + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true, + "requires": { + "esrecurse": "4.2.0", + "estraverse": "4.2.0" + } + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "dev": true + }, + "espree": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.3.tgz", + "integrity": "sha512-Zy3tAJDORxQZLl2baguiRU1syPERAIg0L+JB2MWorORgTu/CplzvxS9WWA7Xh4+Q+eOQihNs/1o1Xep8cvCxWQ==", + "dev": true, + "requires": { + "acorn": "5.4.1", + "acorn-jsx": "3.0.1" + } + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "esquery": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", + "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", + "dev": true, + "requires": { + "estraverse": "4.2.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.2.0", + "object-assign": "4.1.1" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "external-editor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.1.0.tgz", + "integrity": "sha512-E44iT5QVOUJBKij4IIV3uvxuNlbKS38Tw1HiupxEIHPv9qtC2PrDYohbXV5U+1jnfIXttny8gUhj+oZvflFlzA==", + "dev": true, + "requires": { + "chardet": "0.4.2", + "iconv-lite": "0.4.19", + "tmp": "0.0.33" + } + }, + "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=", + "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=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5" + } + }, + "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.3.0", + "object-assign": "4.1.1" + } + }, + "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.1.0", + "pinkie-promise": "2.0.1" + } + }, + "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.3", + "del": "2.2.2", + "graceful-fs": "4.1.11", + "write": "0.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "globals": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.3.0.tgz", + "integrity": "sha512-kkpcKNlmQan9Z5ZmgqKH/SMbSmjxQ7QjyNqfXVc8VJcoBV2UEg+sxQD15GQofGRh2hfpwUb70VC31DR7Rq5Hdw==", + "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.2", + "arrify": "1.0.1", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "dev": true, + "requires": { + "function-bind": "1.1.1" + } + }, + "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.1.1" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "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 + }, + "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==", + "dev": true + }, + "ignore": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", + "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "requires": { + "ansi-escapes": "3.0.0", + "chalk": "2.3.0", + "cli-cursor": "2.1.0", + "cli-width": "2.2.0", + "external-editor": "2.1.0", + "figures": "2.0.0", + "lodash": "4.17.5", + "mute-stream": "0.0.7", + "run-async": "2.3.0", + "rx-lite": "4.0.8", + "rx-lite-aggregates": "4.0.8", + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "through": "2.3.8" + } + }, + "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-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.1.1" + } + }, + "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 + }, + "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.1" + } + }, + "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.2" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "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 + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "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=", + "dev": true + }, + "js-yaml": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", + "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", + "dev": true, + "requires": { + "argparse": "1.0.9", + "esprima": "4.0.0" + } + }, + "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=", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2", + "type-check": "0.3.2" + } + }, + "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.11", + "parse-json": "2.2.0", + "pify": "2.3.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.5", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", + "dev": true + }, + "lodash.cond": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz", + "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "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 + }, + "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.5.0", + "is-builtin-module": "1.0.0", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.1" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "1.2.0" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" + } + }, + "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.2.0" + } + }, + "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 + }, + "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.3.1" + } + }, + "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.1" + } + }, + "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=", + "dev": true + }, + "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-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.3.0" + } + }, + "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.4" + } + }, + "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.1.2" + } + }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "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=", + "dev": true + }, + "progress": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "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.4.0", + "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.1.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==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "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.1" + } + }, + "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 + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "2.0.1", + "signal-exit": "3.0.2" + } + }, + "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.1.2" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "2.1.0" + } + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true + }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "requires": { + "rx-lite": "4.0.8" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0" + } + }, + "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.2.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 + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "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" + } + }, + "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==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "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" + }, + "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 + } + } + }, + "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 + }, + "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 + }, + "table": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "dev": true, + "requires": { + "ajv": "5.5.2", + "ajv-keywords": "2.1.1", + "chalk": "2.3.0", + "lodash": "4.17.5", + "slice-ansi": "1.0.0", + "string-width": "2.1.1" + } + }, + "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 + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "1.0.2" + } + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "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.2", + "spdx-expression-parse": "1.0.4" + } + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "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" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } +} diff --git a/plugins/atom/package.json b/plugins/atom/package.json new file mode 100644 index 0000000..5e9ea82 --- /dev/null +++ b/plugins/atom/package.json @@ -0,0 +1,27 @@ +{ + "name": "tandem", + "main": "./lib/tandem", + "version": "0.0.0", + "description": "Decentralized collaborative, cross-platform editing", + "keywords": [], + "activationCommands": { + "atom-workspace": ["tandem:connect", "tandem:start", "tandem:stop"] + }, + "repository": "https://github.com/geoffxy/tandem", + "license": "MIT", + "engines": { + "atom": ">=1.0.0 <2.0.0" + }, + "dependencies": { + "atom": "^1.1.0", + "child_process": "^1.0.2" + }, + "devDependencies": { + "eslint": "^4.17.0", + "eslint-config-airbnb-base": "^12.1.0", + "eslint-config-standard": "^11.0.0-beta.0", + "eslint-plugin-import": "^2.8.0", + "eslint-plugin-promise": "^3.6.0", + "eslint-plugin-standard": "^3.0.1" + } +} From 3aa19821ad4a89b3d868106320e0b11a36ad373b Mon Sep 17 00:00:00 2001 From: Sameer Chitley Date: Fri, 16 Feb 2018 00:02:42 -0500 Subject: [PATCH 2/9] Add apm link/unlin to dev setup --- plugins/atom/atom_dev_setup.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/atom/atom_dev_setup.sh b/plugins/atom/atom_dev_setup.sh index 66a7c7a..b86f0db 100755 --- a/plugins/atom/atom_dev_setup.sh +++ b/plugins/atom/atom_dev_setup.sh @@ -5,15 +5,20 @@ SCRIPTPATH=$( cd $(dirname $0) ; pwd -P ) UNINSTALL_OPTION="--uninstall" function install() { - uninstall + rm -f agent + rm -f crdt ln -s $SCRIPTPATH/../../agent $SCRIPTPATH ln -s $SCRIPTPATH/../../crdt $SCRIPTPATH + + apm link . } function uninstall() { rm -f agent rm -f crdt + + apm unlink . } if [ "$1" == "$UNINSTALL_OPTION" ] ; then From 5832c22854c22f408fca4e5633f848701fa41c33 Mon Sep 17 00:00:00 2001 From: Sameer Chitley Date: Fri, 16 Feb 2018 00:15:07 -0500 Subject: [PATCH 3/9] Update TODOs and fix formatting --- plugins/atom/lib/tandemPlugin.js | 8 ++++---- plugins/atom/package.json | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/plugins/atom/lib/tandemPlugin.js b/plugins/atom/lib/tandemPlugin.js index cedd1c3..1fa674d 100644 --- a/plugins/atom/lib/tandemPlugin.js +++ b/plugins/atom/lib/tandemPlugin.js @@ -70,7 +70,6 @@ export default class TandemPlugin { return this._isActive; } - // TODO: collect changes and process later once didStopChanging? _sendPatches(event) { if (this._processingMessage) { return; @@ -78,7 +77,7 @@ export default class TandemPlugin { const patches = createPatches(event); const message = new m.NewPatches(patches); - this._agent.stdin.write(m.serialize(message)); // TODO: encode? + this._agent.stdin.write(m.serialize(message)); this._agent.stdin.write('\n'); } @@ -95,7 +94,7 @@ export default class TandemPlugin { if (this._connectTo) { const { hostIP, hostPort } = this._connectTo; const message = new m.ConnectTo(hostIP, hostPort); - this._agent.stdin.write(m.serialize(message)); // TODO: encode? + this._agent.stdin.write(m.serialize(message)); this.agent.stdin.write('\n'); } @@ -148,7 +147,8 @@ export default class TandemPlugin { _shutDownAgent() { this._processingMessage = false; this._subscriptions.dispose(); - this._agent.kill('SIGINT'); // TODO: fix + // TODO: Ensure that the agent is disposed + this._agent.kill('SIGINT'); } start(textBuffer, hostIP, hostPort) { diff --git a/plugins/atom/package.json b/plugins/atom/package.json index 5e9ea82..ec5f172 100644 --- a/plugins/atom/package.json +++ b/plugins/atom/package.json @@ -5,7 +5,11 @@ "description": "Decentralized collaborative, cross-platform editing", "keywords": [], "activationCommands": { - "atom-workspace": ["tandem:connect", "tandem:start", "tandem:stop"] + "atom-workspace": [ + "tandem:connect", + "tandem:start", + "tandem:stop" + ] }, "repository": "https://github.com/geoffxy/tandem", "license": "MIT", From 3e0560452a6d1429936b109c9c59a75263fe73d5 Mon Sep 17 00:00:00 2001 From: Sameer Chitley Date: Fri, 16 Feb 2018 01:54:57 -0500 Subject: [PATCH 4/9] Use atom notifications instead of conosle.log where appropriate --- plugins/atom/lib/tandem.js | 1 - plugins/atom/lib/tandemPlugin.js | 16 +++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/plugins/atom/lib/tandem.js b/plugins/atom/lib/tandem.js index 1607b9f..f608a77 100644 --- a/plugins/atom/lib/tandem.js +++ b/plugins/atom/lib/tandem.js @@ -54,7 +54,6 @@ export default { _start() { this._getTextEditor().then((editor) => { - console.log('starting tandem agent'); this._tandemAgent.start(editor.getBuffer()); }); }, diff --git a/plugins/atom/lib/tandemPlugin.js b/plugins/atom/lib/tandemPlugin.js index 1fa674d..9e04cd8 100644 --- a/plugins/atom/lib/tandemPlugin.js +++ b/plugins/atom/lib/tandemPlugin.js @@ -98,9 +98,7 @@ export default class TandemPlugin { this.agent.stdin.write('\n'); } - const msg = `Bound agent to port ${this._agentPort}`; - console.log(msg); - + atom.notifications.addSuccess(`Bound agent to port ${this._agentPort}`); this._agent.stdout.setEncoding('utf-8'); this._agent.stdout.on('data', this._readAgent.bind(this)); @@ -153,14 +151,12 @@ export default class TandemPlugin { start(textBuffer, hostIP, hostPort) { if (this._isActive) { - const msg = `Cannot start. An instance is already running on :${this._agentPort}`; - console.log(msg); + atom.notifications.addError(`Cannot start. An instance is already running on :${this._agentPort}`); return; } if (!!hostIP && !hostPort) { - const msg = 'Cannot start. IP specified. You must also provide a port'; - console.log(msg); + atom.notifications.addError('Cannot start. IP specified. You must also provide a port'); return; } @@ -177,13 +173,11 @@ export default class TandemPlugin { stop() { if (!this._isActive) { - const msg = 'Cannot stop. No instance running.'; - console.log(msg); + atom.notifications.addError('Cannot stop. No instance running.'); return; } this._shutDownAgent(); - const msg = 'Tandem instance shut down.'; - console.log(msg); + atom.notifications.addInfo('Tandem instance shut down.'); } } From 238b52cd289b79b9b080af349881683bf8de0dc0 Mon Sep 17 00:00:00 2001 From: Sameer Chitley Date: Fri, 16 Feb 2018 02:00:53 -0500 Subject: [PATCH 5/9] Ensure tandem agent is killed --- plugins/atom/lib/tandemPlugin.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/atom/lib/tandemPlugin.js b/plugins/atom/lib/tandemPlugin.js index 9e04cd8..d1cf503 100644 --- a/plugins/atom/lib/tandemPlugin.js +++ b/plugins/atom/lib/tandemPlugin.js @@ -143,10 +143,10 @@ export default class TandemPlugin { } _shutDownAgent() { - this._processingMessage = false; this._subscriptions.dispose(); - // TODO: Ensure that the agent is disposed - this._agent.kill('SIGINT'); + this._processingMessage = false; + this._agent.kill('SIGKILL'); + this._isActive = false; } start(textBuffer, hostIP, hostPort) { From 9eb263c76899ff2c98663ea7b104e5d1e939c506 Mon Sep 17 00:00:00 2001 From: Sameer Chitley Date: Fri, 16 Feb 2018 02:03:29 -0500 Subject: [PATCH 6/9] Delete symlinks and add to gitignore --- plugins/atom/.gitignore | 2 ++ plugins/atom/agent | 1 - plugins/atom/crdt | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 120000 plugins/atom/agent delete mode 120000 plugins/atom/crdt diff --git a/plugins/atom/.gitignore b/plugins/atom/.gitignore index ade14b9..667e127 100644 --- a/plugins/atom/.gitignore +++ b/plugins/atom/.gitignore @@ -1,3 +1,5 @@ .DS_Store npm-debug.log node_modules +agent +crdt diff --git a/plugins/atom/agent b/plugins/atom/agent deleted file mode 120000 index a765cd6..0000000 --- a/plugins/atom/agent +++ /dev/null @@ -1 +0,0 @@ -/Users/sameer/Documents/development/tandem/plugins/atom/../../agent \ No newline at end of file diff --git a/plugins/atom/crdt b/plugins/atom/crdt deleted file mode 120000 index b766af9..0000000 --- a/plugins/atom/crdt +++ /dev/null @@ -1 +0,0 @@ -/Users/sameer/Documents/development/tandem/plugins/atom/../../crdt \ No newline at end of file From 79ad8338ab8cf4ec09356ec991c3f30ccce0d5e0 Mon Sep 17 00:00:00 2001 From: Sameer Chitley Date: Fri, 16 Feb 2018 02:12:30 -0500 Subject: [PATCH 7/9] Rename start/connect/leave commands --- plugins/atom/lib/tandem.js | 6 +++--- plugins/atom/package.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/atom/lib/tandem.js b/plugins/atom/lib/tandem.js index f608a77..1b77f8d 100644 --- a/plugins/atom/lib/tandem.js +++ b/plugins/atom/lib/tandem.js @@ -24,13 +24,13 @@ export default { this._subscriptions = new CompositeDisposable(); this._subscriptions.add(atom.commands.add('atom-workspace', { - 'tandem:connect': () => this._connect(), + 'tandem:join-existing-session': () => this._connect(), })); this._subscriptions.add(atom.commands.add('atom-workspace', { - 'tandem:start': () => this._start(), + 'tandem:start-session': () => this._start(), })); this._subscriptions.add(atom.commands.add('atom-workspace', { - 'tandem:stop': () => this._stop(), + 'tandem:leave-session': () => this._stop(), })); }, diff --git a/plugins/atom/package.json b/plugins/atom/package.json index ec5f172..9657006 100644 --- a/plugins/atom/package.json +++ b/plugins/atom/package.json @@ -6,9 +6,9 @@ "keywords": [], "activationCommands": { "atom-workspace": [ - "tandem:connect", - "tandem:start", - "tandem:stop" + "tandem:join-existing-session", + "tandem:start-session", + "tandem:leave-session" ] }, "repository": "https://github.com/geoffxy/tandem", From 3a16a1f889b79f731e8bc5dd7b66d08b93917d8a Mon Sep 17 00:00:00 2001 From: Sameer Chitley Date: Fri, 16 Feb 2018 03:01:29 -0500 Subject: [PATCH 8/9] Open new editor when connecting to tandem session --- plugins/atom/lib/tandem.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/atom/lib/tandem.js b/plugins/atom/lib/tandem.js index 1b77f8d..b572e6a 100644 --- a/plugins/atom/lib/tandem.js +++ b/plugins/atom/lib/tandem.js @@ -1,6 +1,6 @@ 'use babel'; -import { CompositeDisposable, Disposable } from 'atom'; +import { CompositeDisposable } from 'atom'; import TandemPlugin from './tandemPlugin'; import ConnectView from './connectView'; @@ -41,8 +41,11 @@ export default { this._subscriptions.dispose(); }, - _getTextEditor() { + _getTextEditor(newEditor) { return new Promise((res) => { + if (newEditor) { + return res(atom.workspace.open()); + } const editor = atom.workspace.getActiveTextEditor(); if (editor) { return res(editor); @@ -69,7 +72,7 @@ export default { const ip = args[0]; const port = args[1]; - this._getTextEditor().then((editor) => { + this._getTextEditor(/* newEditor */ true).then((editor) => { this._tandemAgent.start(editor.getBuffer(), ip, port); }); }, From 027f36d6ebe30cf18568a171978ea25662614835 Mon Sep 17 00:00:00 2001 From: Sameer Chitley Date: Fri, 16 Feb 2018 12:24:09 -0500 Subject: [PATCH 9/9] move utilities to utils; fix connect to message --- plugins/atom/lib/tandemPlugin.js | 56 +++++--------------------------- plugins/atom/lib/utils.js | 45 +++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 47 deletions(-) diff --git a/plugins/atom/lib/tandemPlugin.js b/plugins/atom/lib/tandemPlugin.js index d1cf503..209d812 100644 --- a/plugins/atom/lib/tandemPlugin.js +++ b/plugins/atom/lib/tandemPlugin.js @@ -1,52 +1,14 @@ 'use babel'; import { CompositeDisposable } from 'atom'; -import { getStringPort, spawnAgent } from './utils'; +import { + createPatches, + eventForBuffer, + getStringPort, + spawnAgent, +} from './utils'; import * as m from './messages'; -const createPatches = event => - [ - { - start: { - row: event.oldRange.start.row, - column: event.oldRange.start.column, - }, - end: { - row: event.oldRange.end.row, - column: event.oldRange.end.column, - }, - text: '', - }, - { - start: { - row: event.oldRange.start.row, - column: event.oldRange.start.column, - }, - end: { - row: 0, - column: 0, - }, - text: event.newText, - }, - ]; - -const eventForBuffer = (buffer) => { - return { - oldRange: { - start: { - row: 0, - column: 0, - }, - end: { - row: 0, - column: 0, - }, - }, - newText: buffer.getText(), - }; -}; - - export default class TandemPlugin { constructor() { this._isActive = false; @@ -93,9 +55,9 @@ export default class TandemPlugin { if (this._connectTo) { const { hostIP, hostPort } = this._connectTo; - const message = new m.ConnectTo(hostIP, hostPort); + const message = new m.ConnectTo(hostIP, parseInt(hostPort, 10)); this._agent.stdin.write(m.serialize(message)); - this.agent.stdin.write('\n'); + this._agent.stdin.write('\n'); } atom.notifications.addSuccess(`Bound agent to port ${this._agentPort}`); @@ -160,7 +122,7 @@ export default class TandemPlugin { return; } - this._connectTo = hostIP ? Object.freeze({ ip: hostIP, port: hostPort }) : null; + this._connectTo = hostIP ? Object.freeze({ hostIP, hostPort }) : null; this._initialize(textBuffer); this._startAgent(); diff --git a/plugins/atom/lib/utils.js b/plugins/atom/lib/utils.js index 79ceb58..12e09d9 100644 --- a/plugins/atom/lib/utils.js +++ b/plugins/atom/lib/utils.js @@ -23,7 +23,52 @@ const random = (lo, hi) => { const getStringPort = () => String(random(60600, 62600)); +const createPatches = event => + [ + { + start: { + row: event.oldRange.start.row, + column: event.oldRange.start.column, + }, + end: { + row: event.oldRange.end.row, + column: event.oldRange.end.column, + }, + text: '', + }, + { + start: { + row: event.oldRange.start.row, + column: event.oldRange.start.column, + }, + end: { + row: 0, + column: 0, + }, + text: event.newText, + }, + ]; + +const eventForBuffer = buffer => ( + { + oldRange: { + start: { + row: 0, + column: 0, + }, + end: { + row: 0, + column: 0, + }, + }, + newText: buffer.getText(), + } +); + + export default { + createPatches, + eventForBuffer, spawnAgent, getStringPort, };