New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

big-dig

Package Overview
Dependencies
Maintainers
5
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

big-dig - npm Package Compare versions

Comparing version 0.7.1 to 0.7.2

__tests__/client/utils/asyncRequest-test.js

4

loadTranspiler.js

@@ -16,3 +16,3 @@ /**

prefer-object-spread/prefer-object-spread: 0,
rulesdir/no-commonjs: 0,
nuclide-internal/no-commonjs: 0,
*/

@@ -23,4 +23,4 @@

if (fs.existsSync(path.join(__dirname, 'DEVELOPMENT'))) {
// eslint-disable-next-line rulesdir/modules-dependencies
// eslint-disable-next-line nuclide-internal/modules-dependencies
require('../nuclide-node-transpiler');
}
{
"name": "big-dig",
"version": "0.7.1",
"version": "0.7.2",
"description": "Secure, re-connectable channel for bidirectional communication with a remote host.",

@@ -10,10 +10,11 @@ "author": "Nuclide : Remote",

"prepublish": "../scripts/prepublish.sh",
"test": "node ../nuclide-jest/bin/jest-node.js"
"test": "true",
"jest": "../../jest/fb-jest big-dig"
},
"dependencies": {
"async-to-generator": "1.1.0",
"double-ended-queue": "2.1.0-0",
"event-kit": "2.2.0",
"log4js": "1.1.1",
"nuclide-commons": "0.7.1",
"nuclide-commons": "0.7.2",
"nuclide-watchman-helpers": "0.7.2",
"request": "2.79.0",

@@ -24,2 +25,3 @@ "rimraf": "2.6.2",

"temp": "0.8.3",
"thrift": "^0.11.0",
"uuid": "3.0.1",

@@ -29,4 +31,4 @@ "ws": "3.2.0"

"devDependencies": {
"nuclide-jest": "0.7.1"
"nuclide-jest": "0.7.2"
}
}

@@ -26,3 +26,9 @@ # Big Dig

* Server must have `openssl` available on the `$PATH`.
* Server must have Node 6.5.0 or later installed.
* Server must have Node 7.9.0 or later installed.
* Server must have [Watchman](https://facebook.github.io/watchman/) installed
in order for the file-watching API to work correctly.
* Server must have [`rg`](https://github.com/BurntSushi/ripgrep) installed
in order for text search to work correctly.
* Server must have [`hg`](https://www.mercurial-scm.org/) installed in order
for the Mercurial integration to work correctly.
* Minimal privileges required by the user on the client:

@@ -29,0 +35,0 @@ * Client must be able to make a single `ssh` connection to the server in order

@@ -1,35 +0,98 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.BigDigClient = undefined;
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.BigDigClient = void 0;
var _RxMin = require("rxjs/bundles/Rx.min.js");
function _log4js() {
const data = require("log4js");
_log4js = function () {
return data;
};
return data;
}
function _BigDigServer() {
const data = require("../server/BigDigServer");
_BigDigServer = function () {
return data;
};
return data;
}
function _types() {
const data = require("../services/thrift/types");
_types = function () {
return data;
};
return data;
}
function _TunnelManager() {
const data = require("../services/tunnel/TunnelManager");
_TunnelManager = function () {
return data;
};
return data;
}
var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js');var _log4js;
function _load_log4js() {return _log4js = require('log4js');}
function _ThriftClientManager() {
const data = require("../services/thrift/ThriftClientManager");
/**
* This class is responsible for talking to a Big Dig server, which enables the
* client to launch a remote process and communication with its stdin, stdout,
* and stderr.
*/
class BigDigClient {
_ThriftClientManager = function () {
return data;
};
return data;
}
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
/**
* This class is responsible for talking to a Big Dig server, which enables the
* client to launch a remote process and communication with its stdin, stdout,
* and stderr.
*/
class BigDigClient {
constructor(reliableSocketTransport) {
this._logger = (0, (_log4js || _load_log4js()).getLogger)();
this._logger = (0, _log4js().getLogger)();
this._transport = reliableSocketTransport;
this._tagToSubject = new Map();
this._tunnelManager = new (_TunnelManager().TunnelManager)({
onMessage: () => {
return this.onMessage('tunnel');
},
send: message => {
this.sendMessage('tunnel', message);
}
});
this._thriftClientManager = new (_ThriftClientManager().ThriftClientManager)({
onMessage: () => {
return this.onMessage(_types().THRIFT_SERVICE_TAG);
},
send: message => {
this.sendMessage(_types().THRIFT_SERVICE_TAG, message);
}
}, this._tunnelManager);
const observable = reliableSocketTransport.onMessage();

@@ -41,3 +104,5 @@ observable.subscribe({

const tag = message.substring(0, index);
const subject = this._tagToSubject.get(tag);
if (subject != null) {

@@ -50,9 +115,12 @@ const body = message.substring(index + 1);

},
error(err) {
this._logger.error('Error received in ConnectionWrapper', err);
},
complete() {
this._logger.error('ConnectionWrapper completed()?');
} });
}
});
}

@@ -68,3 +136,25 @@

async createTunnel(localPort, remotePort, isReverse = false, useIPv4 = false) {
if (!isReverse) {
return this._tunnelManager.createTunnel(localPort, remotePort, useIPv4);
} else {
return this._tunnelManager.createReverseTunnel(localPort, remotePort, useIPv4);
}
}
getOrCreateThriftClient(serviceName) {
return this._thriftClientManager.createThriftClient(serviceName);
}
close() {
this._logger.info('close called');
this._tunnelManager.close();
this._thriftClientManager.close();
if (!this.isClosed()) {
this.sendMessage(_BigDigServer().CLOSE_TAG, '');
}
this._transport.close();

@@ -74,3 +164,11 @@ }

sendMessage(tag, body) {
this._transport.send(`${tag}\0${body}`);
const message = `${tag}\0${body}`;
if (this.isClosed()) {
this._logger.warn(`Attempting to send message to ${this.getAddress()} on closed BigDigClient: ${message}`);
return;
}
this._transport.send(message);
}

@@ -80,6 +178,9 @@

let subject = this._tagToSubject.get(tag);
if (subject == null) {
subject = new _rxjsBundlesRxMinJs.Subject();
subject = new _RxMin.Subject();
this._tagToSubject.set(tag, subject);
}
return subject.asObservable();

@@ -94,12 +195,6 @@ }

return this._transport.getAddress();
}}exports.BigDigClient = BigDigClient; /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
}
}
exports.BigDigClient = BigDigClient;

@@ -1,29 +0,24 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
const CONNECTION_EVENT = 'nuclide-remote-connection';
const CONNECTION_EVENT = 'nuclide-remote-connection'; /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/class ConnectionTracker {constructor(config) {this._config = config;
class ConnectionTracker {
constructor(config) {
this._config = config;
this._expired = false;

@@ -51,7 +46,3 @@ this._connectionStartTime = Date.now();

_trackConnectionResult(
succeed,
errorType,
e)
{
_trackConnectionResult(succeed, errorType, e) {
if (this._expired) {

@@ -61,19 +52,9 @@ return;

const preYubikeyDuration =
this._promptYubikeyTime > 0 ?
this._promptYubikeyTime - this._connectionStartTime :
0;
const postYubikeyDuration =
this._finishYubikeyTime > 0 ? Date.now() - this._finishYubikeyTime : 0;
const realDuration =
preYubikeyDuration > 0 && postYubikeyDuration > 0 ?
preYubikeyDuration + postYubikeyDuration :
0;
const preYubikeyDuration = this._promptYubikeyTime > 0 ? this._promptYubikeyTime - this._connectionStartTime : 0;
const postYubikeyDuration = this._finishYubikeyTime > 0 ? Date.now() - this._finishYubikeyTime : 0;
const realDuration = preYubikeyDuration > 0 && postYubikeyDuration > 0 ? preYubikeyDuration + postYubikeyDuration : 0;
track(CONNECTION_EVENT, {
error: succeed ? '0' : '1',
errorType: errorType || '',
exception: e ?
`name: ${e.name}, message: ${e.message}, stack: ${e.stack}.` :
'',
exception: e ? `name: ${e.name}, message: ${e.message}, stack: ${e.stack}.` : '',
duration: (Date.now() - this._connectionStartTime).toString(),

@@ -87,9 +68,11 @@ preYubikeyDuration: preYubikeyDuration.toString(),

remoteServer: this._config.remoteServer,
authMethod: this._config.authMethod });
authMethod: this._config.authMethod
});
this._expired = true;
}
}
this._expired = true;
}}exports.default = ConnectionTracker;
exports.default = ConnectionTracker;
function track(eventName, metaData) {}

@@ -1,60 +0,78 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });var _asyncToGenerator = _interopRequireDefault(require('async-to-generator'));var _BigDigServer;
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
function _BigDigServer() {
const data = require("../server/BigDigServer");
_BigDigServer = function () {
return data;
};
return data;
}
function _BigDigClient() {
const data = require("./BigDigClient");
_BigDigClient = function () {
return data;
};
return data;
}
function _ReliableSocket() {
const data = require("../socket/ReliableSocket");
_ReliableSocket = function () {
return data;
};
return data;
}
function _load_BigDigServer() {return _BigDigServer = require('../server/BigDigServer');}var _BigDigClient;
function _load_BigDigClient() {return _BigDigClient = require('./BigDigClient');}var _ReliableSocket;
function _load_ReliableSocket() {return _ReliableSocket = require('../socket/ReliableSocket');}function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* strict-local
* @format
*/
/**
* Creates a Big Dig client that speaks the v1 protocol.
*/exports.default = (() => {var _ref = (0, _asyncToGenerator.default)(
function* (
config)
{
const reliableSocket = createReliableSocket(config);
const client = new (_BigDigClient || _load_BigDigClient()).BigDigClient(reliableSocket);
try {
// Make sure we're able to make the initial connection
yield reliableSocket.testConnection();
return client;
} catch (error) {
client.close();
throw error;
}
});function createBigDigClient(_x) {return _ref.apply(this, arguments);}return createBigDigClient;})(); /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/function createReliableSocket(config) {const options = { ca: config.certificateAuthorityCertificate, cert: config.clientCertificate, key: config.clientKey, family: config.family };const serverUri = `https://${config.host}:${config.port}/v1`;
* Creates a Big Dig client that speaks the v1 protocol.
*/
var createBigDigClient = async function createBigDigClient(config) {
const reliableSocket = createReliableSocket(config);
const client = new (_BigDigClient().BigDigClient)(reliableSocket);
const reliableSocket = new (_ReliableSocket || _load_ReliableSocket()).ReliableSocket(
serverUri, (_BigDigServer || _load_BigDigServer()).HEARTBEAT_CHANNEL,
try {
// Make sure we're able to make the initial connection
await reliableSocket.testConnection();
return client;
} catch (error) {
client.close();
throw error;
}
};
options);
exports.default = createBigDigClient;
function createReliableSocket(config) {
const options = {
ca: config.certificateAuthorityCertificate,
cert: config.clientCertificate,
key: config.clientKey,
family: config.family
};
const serverUri = `https://${config.host}:${config.port}/v1`;
const reliableSocket = new (_ReliableSocket().ReliableSocket)(serverUri, _BigDigServer().HEARTBEAT_CHANNEL, options, config.protocolLogger);

@@ -61,0 +79,0 @@ if (!config.ignoreIntransientErrors) {

@@ -16,3 +16,3 @@ /**

prefer-object-spread/prefer-object-spread: 0,
rulesdir/no-commonjs: 0,
nuclide-internal/no-commonjs: 0,
*/

@@ -19,0 +19,0 @@

@@ -1,15 +0,55 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });var _BigDigClient;function _load_BigDigClient() {return _BigDigClient = require('./BigDigClient');}Object.defineProperty(exports, 'BigDigClient', { enumerable: true, get: function () {return (_BigDigClient || _load_BigDigClient()).
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "BigDigClient", {
enumerable: true,
get: function () {
return _BigDigClient().BigDigClient;
}
});
Object.defineProperty(exports, "SshHandshake", {
enumerable: true,
get: function () {
return _SshHandshake().SshHandshake;
}
});
Object.defineProperty(exports, "createBigDigClient", {
enumerable: true,
get: function () {
return _createBigDigClient().default;
}
});
function _BigDigClient() {
const data = require("./BigDigClient");
_BigDigClient = function () {
return data;
};
return data;
}
function _SshHandshake() {
const data = require("./SshHandshake");
_SshHandshake = function () {
return data;
};
return data;
}
function _createBigDigClient() {
const data = _interopRequireDefault(require("./createBigDigClient"));
_createBigDigClient = function () {
return data;
};
return data;
}
BigDigClient;} });var _SshHandshake;function _load_SshHandshake() {return _SshHandshake = require('./SshHandshake');}Object.defineProperty(exports, 'SshHandshake', { enumerable: true, get: function () {return (_SshHandshake || _load_SshHandshake()).
SshHandshake;} });var _createBigDigClient;function _load_createBigDigClient() {return _createBigDigClient = require('./createBigDigClient');}Object.defineProperty(exports, 'createBigDigClient', { enumerable: true, get: function () {return _interopRequireDefault(_createBigDigClient || _load_createBigDigClient()).
default;} });function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -1,47 +0,51 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });var _asyncToGenerator = _interopRequireDefault(require('async-to-generator'));
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _dns = _interopRequireDefault(require("dns"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* strict
* @format
*/
var lookupPreferIpv6 = async function lookupPreferIpv6(host) {
try {
return await lookup(host, 6);
} catch (e) {
if (e.code === 'ENOTFOUND') {
return lookup(host, 4);
}
throw e;
}
};
exports.default = lookupPreferIpv6;
function lookup(host, family) {
return new Promise((resolve, reject) => {
_dns.default.lookup(host, family, (error, address, resultFamily) => {
if (error) {
reject(error);
} else if (address != null) {
if (!(resultFamily === 4 || resultFamily === 6)) {
throw new Error("Invariant violation: \"resultFamily === 4 || resultFamily === 6\"");
}
var _dns = _interopRequireDefault(require('dns'));function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}exports.default = (() => {var _ref = (0, _asyncToGenerator.default)(
function* (
host)
{
try {
return yield lookup(host, 6);
} catch (e) {
if (e.code === 'ENOTFOUND') {
return lookup(host, 4);
}
throw e;
}
});function lookupPreferIpv6(_x) {return _ref.apply(this, arguments);}return lookupPreferIpv6;})(); /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/function lookup(host, family) {return new Promise((resolve, reject) => {_dns.default.lookup(host, family, (error, address, resultFamily) => {if (error) {reject(error);} else if (address != null) {if (!(
resultFamily === 4 || resultFamily === 6)) {throw new Error('Invariant violation: "resultFamily === 4 || resultFamily === 6"');}
resolve({ address, family: resultFamily });
resolve({
address,
family: resultFamily
});
} else {

@@ -51,4 +55,3 @@ reject(new Error('One of error or address must be set.'));

});
});
}

@@ -1,193 +0,156 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.CorruptManifestError = undefined;exports.
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createManifest = createManifest;
exports.serializeManifest = serializeManifest;
exports.deserializeManifest = deserializeManifest;
exports.compareManifests = compareManifests;
exports.CorruptManifestError = void 0;
var _crypto = _interopRequireDefault(require("crypto"));
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
/**
* Thrown when a manifest does not have a valid structure.
*/
class CorruptManifestError extends Error {}
/**
* Verifies that the manifest has a valid structure; throws `ManifestError` if not.
* @param {*} manifest an (untrusted) manifest object.
*/
exports.CorruptManifestError = CorruptManifestError;
function verifyManifest(manifest) {
if (typeof manifest.version !== 'string') {
throw corrupt('Invalid version in manifest');
}
if (typeof manifest.hash !== 'string') {
throw corrupt('Invalid hash property in manifest');
}
const {
version,
hash
} = manifest;
return {
version,
hash
};
}
/**
* Creates a manifest from some observed state.
* @param version The version to assign to the observed state.
* @param manifestFilename The *absolute* path to the manifest; it is used to elide the manifest
* from itself.
* @param basePath The *absolute* path to the root folder of the observed files.
* (Also used to elide the manifest from itself.)
* @param files A list of all [sub]files and [sub]directories under `basePath`, paired with the
* output of `stat` on each file. These should be absolute paths.
*/
function createManifest(version, manifestFilename, basePath, files) {
const hash = _crypto.default.createHash('sha1');
const sortedFiles = files // Exclude the manifest file and directories from the manifest.
.filter(file => file.filename !== manifestFilename && !file.stats.isDirectory()).sort((a, b) => a.filename.localeCompare(b.filename));
for (const file of sortedFiles) {
const filename = _path.default.relative(basePath, file.filename);
const {
mode,
uid,
gid,
size,
mtime
} = file.stats;
hash.update(`${filename}:${mode}.${uid}.${gid}.${size}.${mtime}`, 'utf8');
}
return {
version,
hash: hash.digest('hex')
};
}
/**
* Serializes the manifest into a `Buffer` that can be written to a manifest file.
* @param manifest
*/
function serializeManifest(manifest) {
return new Buffer(JSON.stringify(manifest), 'utf8');
}
/**
* Deserialize data read from a manifest file and verify that it is a valid manifest.
* Throws `ManifestError` if `data` does not encode a valid manifest structure.
* @param data A buffer containing the manifest data loaded from a file.
*/
function deserializeManifest(data) {
return verifyManifest(parseManifest(data.toString('utf8')));
}
/**
* Compares two manifests for agreement on the state of the system.
* NOTICE: equal manifests do not guarantee that the contents of a file has not changed.
* Return a status of:
* * 'equal' if they agree on the state of the system.
* * 'diff-versions' if the manifests do not have the same version
* * 'changed-files' a file has been added, removed, or had its stats changed.
*
* @param expected The state we expect to see.
* @param current The observed state.
* @return `{status: 'okay'}` if the two manifests agree on the state of the system.
*/
function compareManifests(expected, current) {
if (expected.version !== current.version) {
return {
status: 'diff-versions',
message: 'Different versions'
};
} else if (expected.hash !== current.hash) {
return {
status: 'changed-files',
message: 'Files are missing, have been added, or file-stats have changed'
};
}
return {
status: 'equal'
};
}
function corrupt(message) {
return new CorruptManifestError(message);
}
createManifest = createManifest;exports.
serializeManifest = serializeManifest;exports.
deserializeManifest = deserializeManifest;exports.
compareManifests = compareManifests;var _crypto = _interopRequireDefault(require('crypto'));var _path = _interopRequireDefault(require('path'));function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };} /**
* Thrown when a manifest does not have a valid structure.
*/ /**
* Represents a "hash" of the state of a file tree. Ideally, comparing a stored manifest with a
* current manifest created from the (current) state of the file tree will tell us if an
* outside party has modified the contents of the file tree. ***This is only intended to detect
* accidental changes.***
*
* The approach is to a hash the list of the files within the tree, along with some of their
* `stats`, e.g. its last-modified time and file size. This does not hash the contents of the files
* themselves. (The justification is that any change to the file is likely to also change its size
* or last-modified time).
*/ /**
* Convenience type to document how we expect a manifest to look when we read it from a file. But
* without trusting that the structure is valid.
*/ /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/class CorruptManifestError extends Error {}exports.CorruptManifestError = CorruptManifestError; /**
* Verifies that the manifest has a valid structure; throws `ManifestError` if not.
* @param {*} manifest an (untrusted) manifest object.
*/function verifyManifest(manifest) {if (typeof manifest.version !== 'string') {throw corrupt('Invalid version in manifest');}if (typeof manifest.hash !== 'string') {throw corrupt('Invalid hash property in manifest');}const { version, hash } = manifest;return { version, hash };} /**
* Creates a manifest from some observed state.
* @param version The version to assign to the observed state.
* @param manifestFilename The *absolute* path to the manifest; it is used to elide the manifest
* from itself.
* @param basePath The *absolute* path to the root folder of the observed files.
* (Also used to elide the manifest from itself.)
* @param files A list of all [sub]files and [sub]directories under `basePath`, paired with the
* output of `stat` on each file. These should be absolute paths.
*/function createManifest(version, manifestFilename, basePath, files) {const hash = _crypto.default.createHash('sha1');const sortedFiles = files // Exclude the manifest file and directories from the manifest.
.filter(file => file.filename !== manifestFilename && !file.stats.isDirectory()).sort((a, b) => a.filename.localeCompare(b.filename));for (const file of sortedFiles) {const filename = _path.default.relative(basePath, file.filename);const { mode, uid, gid, size, mtime } = file.stats;hash.update(`${filename}:${mode}.${uid}.${gid}.${size}.${mtime}`, 'utf8');}return { version, hash: hash.digest('hex') };} /**
* Serializes the manifest into a `Buffer` that can be written to a manifest file.
* @param manifest
*/function serializeManifest(manifest) {return new Buffer(JSON.stringify(manifest), 'utf8');} /**
* Deserialize data read from a manifest file and verify that it is a valid manifest.
* Throws `ManifestError` if `data` does not encode a valid manifest structure.
* @param data A buffer containing the manifest data loaded from a file.
*/function deserializeManifest(data) {return verifyManifest(parseManifest(data.toString('utf8')));} /**
* Compares two manifests for agreement on the state of the system.
* NOTICE: equal manifests do not guarantee that the contents of a file has not changed.
* Return a status of:
* * 'equal' if they agree on the state of the system.
* * 'diff-versions' if the manifests do not have the same version
* * 'changed-files' a file has been added, removed, or had its stats changed.
*
* @param expected The state we expect to see.
* @param current The observed state.
* @return `{status: 'okay'}` if the two manifests agree on the state of the system.
*/function compareManifests(expected, current) {if (expected.version !== current.version) {return { status: 'diff-versions', message: 'Different versions' };} else if (expected.hash !== current.hash) {return { status: 'changed-files', message: 'Files are missing, have been added, or file-stats have changed' };}return { status: 'equal' };}function corrupt(message) {return new CorruptManifestError(message);}function parseManifest(data) {try {return JSON.parse(data);} catch (error) {throw corrupt('Manifest is not valid JSON syntax');}}
function parseManifest(data) {
try {
return JSON.parse(data);
} catch (error) {
throw corrupt('Manifest is not valid JSON syntax');
}
}

@@ -1,581 +0,471 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.InstallError = undefined;var _asyncToGenerator = _interopRequireDefault(require('async-to-generator'));let runPackage = (() => {var _ref = (0, _asyncToGenerator.default)(
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getPackage = getPackage;
exports.InstallError = void 0;
var _fs = _interopRequireDefault(require("fs"));
var _path = _interopRequireDefault(require("path"));
function _string() {
const data = require("nuclide-commons/string");
_string = function () {
return data;
};
return data;
}
function _log4js() {
const data = require("log4js");
_log4js = function () {
return data;
};
return data;
}
function _Manifest() {
const data = require("./Manifest");
_Manifest = function () {
return data;
};
return data;
}
function _SftpClient() {
const data = require("./SftpClient");
_SftpClient = function () {
return data;
};
return data;
}
function _SshClient() {
const data = require("./SshClient");
_SshClient = function () {
return data;
};
return data;
}
function _events() {
const data = require("../common/events");
_events = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* strict-local
* @format
*/
function okay() {
(0, _log4js().getLogger)().info('Verify installation: OKAY.');
return {
status: 'okay'
};
}
function needsUpdate(expected, current) {
const message = `Found remote server version ${current}, but expected version ${expected}.`;
(0, _log4js().getLogger)().info(`Verify installation: ${message}`);
return {
status: 'needs-update',
message,
expected,
current
};
}
function corrupt(message, reason) {
(0, _log4js().getLogger)().info(`Verify installation: CORRUPT - ${message}`);
return {
status: 'corrupt',
reason,
message
};
}
function needsInstall(message) {
(0, _log4js().getLogger)().info(`Verify installation: NEEDS INSTALL - ${message}`);
return {
status: 'needs-install',
message
};
}
class InstallError extends Error {
constructor(message, stdout) {
super(message + '\n' + (stdout || ''));
this.message = message;
this.stdout = stdout;
}
}
exports.InstallError = InstallError;
/**
* Creates an abstraction for the remote package, which may be an unmanaged (preexisting)
* installation, or a managed installation by big-dig (which may need installation).
* @param pkg
*/
function getPackage(pkg) {
if (pkg.package) {
return new ManagedPackage(pkg);
} else {
return new UnmanagedPackage(pkg);
}
}
async function runPackage(command, cwd, options, ssh) {
const cmd = cwd == null ? `${command}` : `cd ${cwd} && ${command}`;
const {
stdout,
result
} = await ssh.exec(cmd, options); // Collect any stdout in case there is an error.
let output = '';
stdout.subscribe(data => output += data); // Wait for the bootstrapper to finish
const {
code
} = await result;
return {
stdout: output,
code
};
}
/**
* An unmanaged, preexisting package. We know nothing about what the package looks like, except how
* to invoke it. The user is responsible for installing the package on the remote machine and
* providing a valid command for invocation.
*/
class UnmanagedPackage {
constructor(params) {
this._package = params;
}
run(args, options = {}, ssh) {
const {
cwd,
command
} = this._package;
const cmd = `${command} ${(0, _string().shellQuote)(args)}`;
return runPackage(cmd, cwd, Object.assign({}, options), ssh);
}
async verifyInstallation(ssh) {
return okay();
}
async install(ssh, options) {
throw new InstallError('An unmanaged remote server cannot be installed.');
}
getInstallationPath() {
throw new Error('Cannot determine the installation path of an unmanaged remote server.');
}
}
/**
* A managed package. It's our responsibility to verify that it is installed correctly. Implements
* `install` and `getInstallationPath`.
*/
class ManagedPackage {
/** We record the version when verifying an installation, and later use it when requesting an
* updated package to install (allowing the packager to provide a delta). If the installation
* cannot be verified or we are pursuing a fresh install, then this will be `null`.
*/
constructor(params) {
this._currentVersion = null;
this._package = params;
}
run(args, options = {}, ssh) {
const {
installationPath,
command
} = this._package;
const cmd = command(installationPath, (0, _string().shellQuote)(args));
return runPackage(cmd, installationPath, Object.assign({}, options), ssh);
}
async verifyInstallation(ssh) {
const pkg = this._package;
const sftp = await ssh.sftp();
const manifestFile = this._manifestPath();
(0, _log4js().getLogger)().info('Verifying installation...');
try {
if (!(await sftp.exists(pkg.installationPath))) {
return needsInstall('Installation path does not exist');
}
const installPathStats = await sftp.lstat(pkg.installationPath);
if (!installPathStats.isDirectory()) {
return corrupt('Installation path already exists as a file', 'install-path-is-file');
} else if ((await sftp.readdir(pkg.installationPath)).length === 0) {
return needsInstall('Installation path is empty');
} else if (!(await sftp.exists(manifestFile))) {
return corrupt(`Manifest does not exist at ${manifestFile}`, 'missing-manifest');
} // The installation state we expect to see:
const manifest = (0, _Manifest().deserializeManifest)((await sftp.readFile(manifestFile)));
this._currentVersion = manifest.version; // The actual state:
const currentFileTree = await sftp.filetree(pkg.installationPath);
const current = (0, _Manifest().createManifest)(manifest.version
/** Assume current version = manifest version */
, manifestFile, pkg.installationPath, currentFileTree); // Is the manifest correct?
const check = (0, _Manifest().compareManifests)(manifest, current);
if ( // Should be equivalent to `check.status !== 'okay'`, but Flow is not smart enough :(
check.status === 'changed-files' || check.status === 'diff-versions' // This case should not happen
) {
return corrupt(check.message, check.status);
} // The manifest is correct!
// Do we need to upgrade the package?
const expectedVersion = await pkg.expectedVersion(manifest.version);
if (expectedVersion !== manifest.version) {
return needsUpdate(expectedVersion, manifest.version);
}
return okay();
} finally {
await sftp.end();
}
}
async install(ssh, options) {
const pkg = this._package;
let sftp = await ssh.sftp();
const opts = Object.assign({}, options);
opts.name = opts.name !== undefined ? opts.name : 'package';
opts.tempDir = opts.tempDir != null ? opts.tempDir : '/tmp/';
opts.force = opts.force || false;
try {
(0, _log4js().getLogger)().info(`Installing ${opts.name}...`);
const archive = await pkg.package(this._currentVersion);
const cleanFirst = archive.isDelta !== true; // If we're not installing a delta and we believe there are existing files that need to be
// removed (e.g. we're updating the installation), then first remove the existing files.
if (cleanFirst && opts.force) {
await this._uninstall(sftp);
this._currentVersion = null;
}
await this._initInstallationPath(sftp, {
assertEmpty: cleanFirst
});
if (pkg.extract) {
const extract = pkg.extract; // Transfer the archive to a file first? Otherwise, it will be streamed to the extractor.
if (extract.fromFileCommand) {
const tmp = _path.default.join(opts.tempDir, `big-dig-package.${Math.random()}`);
(0, _log4js().getLogger)().info(`Install: transferring package to temporary file (${tmp})...`);
await this._transferToFile(archive, tmp, sftp);
await sftp.end();
try {
(0, _log4js().getLogger)().info('Install: extracting package...');
await this._extractFromFile(extract, tmp, ssh);
} finally {
try {
sftp = await ssh.sftp();
await sftp.unlink(tmp);
await sftp.end();
} catch (error) {
(0, _log4js().getLogger)().warn(`Install: could not remove temporary file ${tmp}.`);
}
}
} else {
await sftp.end();
(0, _log4js().getLogger)().info('Install: transferring and extracting package...');
await this._extractFromStdin(archive, extract, ssh);
}
sftp = await ssh.sftp();
} else {
(0, _log4js().getLogger)().info('Install: transferring package...');
await this._transferToFile(archive, pkg.installationPath, sftp);
}
function* (
command,
cwd,
options,
ssh)
{
const cmd = cwd == null ? `${command}` : `cd ${cwd} && ${command}`;
const { stdout, result } = yield ssh.exec(cmd, options);
// Collect any stdout in case there is an error.
let output = '';
stdout.subscribe(function (data) {return output += data;});
// Wait for the bootstrapper to finish
const { code } = yield result;
return { stdout: output, code };
});return function runPackage(_x, _x2, _x3, _x4) {return _ref.apply(this, arguments);};})();
/**
* An unmanaged, preexisting package. We know nothing about what the package looks like, except how
* to invoke it. The user is responsible for installing the package on the remote machine and
* providing a valid command for invocation.
*/exports.getPackage = getPackage;var _fs = _interopRequireDefault(require('fs'));var _path = _interopRequireDefault(require('path'));var _string;function _load_string() {return _string = require('nuclide-commons/string');}var _log4js;function _load_log4js() {return _log4js = require('log4js');}var _Manifest;function _load_Manifest() {return _Manifest = require('./Manifest');}var _SftpClient;function _load_SftpClient() {return _SftpClient = require('./SftpClient');}var _SshClient;function _load_SshClient() {return _SshClient = require('./SshClient');}var _events;function _load_events() {return _events = require('../common/events');}function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };} /** Install the package as-needed. Big-dig saves a remote manifest, which includes the version and
* the `stat` for each file. This is used to determine whether the package needs to be
* (re)installed. */ /** Assume the package is already installed and not managed by Big-dig. */ /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/function okay() {(0, (_log4js || _load_log4js()).getLogger)().info('Verify installation: OKAY.');return { status: 'okay' };}function needsUpdate(expected, current) {const message = `Found remote server version ${current}, but expected version ${expected}.`;(0, (_log4js || _load_log4js()).getLogger)().info(`Verify installation: ${message}`);return { status: 'needs-update', message, expected, current };}function corrupt(message, reason) {(0, (_log4js || _load_log4js()).getLogger)().info(`Verify installation: CORRUPT - ${message}`);return { status: 'corrupt', reason, message };}function needsInstall(message) {(0, (_log4js || _load_log4js()).getLogger)().info(`Verify installation: NEEDS INSTALL - ${message}`);return { status: 'needs-install', message };}class InstallError extends Error {constructor(message, stdout) {super(message + '\n' + (stdout || ''));this.message = message;this.stdout = stdout;}}exports.InstallError = InstallError; /**
* Creates an abstraction for the remote package, which may be an unmanaged (preexisting)
* installation, or a managed installation by big-dig (which may need installation).
* @param pkg
*/function getPackage(pkg) {if (pkg.package) {return new ManagedPackage(pkg);} else {return new UnmanagedPackage(pkg);}}class UnmanagedPackage {constructor(params) {this._package = params;}run(args, options = {}, ssh) {const { cwd, command } = this._package;const cmd = `${command} ${(0, (_string || _load_string()).shellQuote)(args)}`;return runPackage(cmd, cwd, Object.assign({}, options), ssh);}
verifyInstallation(ssh) {return (0, _asyncToGenerator.default)(function* () {
return okay();})();
(0, _log4js().getLogger)().info('Install: saving manifest...');
await this._saveManifest(archive.version, sftp);
this._currentVersion = archive.version;
(0, _log4js().getLogger)().info('Install: complete.');
} catch (error) {
(0, _log4js().getLogger)().info(`Install: FAILURE (${error.message || error.toString()})`);
throw error;
} finally {
await sftp.end();
}
}
/**
* Returns the remote path of the package.
*/
install(ssh, options) {return (0, _asyncToGenerator.default)(function* () {
throw new InstallError('An unmanaged remote server cannot be installed.');})();
}
getInstallationPath() {
throw new Error(
'Cannot determine the installation path of an unmanaged remote server.');
return this._package.installationPath;
}
/**
* Assuming that the package files have all been installed, this creates and saves a manifest of
* the state of the installation.
*/
}}
/**
* A managed package. It's our responsibility to verify that it is installed correctly. Implements
* `install` and `getInstallationPath`.
*/
class ManagedPackage {
constructor(params) {this._currentVersion = null;
this._package = params;
} /** We record the version when verifying an installation, and later use it when requesting an
* updated package to install (allowing the packager to provide a delta). If the installation
* cannot be verified or we are pursuing a fresh install, then this will be `null`.
*/run(args,
options = {},
ssh)
{
const { installationPath, command } = this._package;
const cmd = command(installationPath, (0, (_string || _load_string()).shellQuote)(args));
return runPackage(cmd, installationPath, Object.assign({}, options), ssh);
async _saveManifest(version, sftp) {
const installPath = this._package.installationPath;
const currentFileTree = await sftp.filetree(installPath);
const current = (0, _Manifest().createManifest)(version, this._manifestPath(), installPath, currentFileTree);
const data = (0, _Manifest().serializeManifest)(current);
await sftp.writeFile(this._manifestPath(), data);
}
/**
* Removes all (preexisting) files from the installation path.
*/
verifyInstallation(ssh) {var _this = this;return (0, _asyncToGenerator.default)(function* () {
const pkg = _this._package;
const sftp = yield ssh.sftp();
const manifestFile = _this._manifestPath();
(0, (_log4js || _load_log4js()).getLogger)().info('Verifying installation...');
try {
if (!(yield sftp.exists(pkg.installationPath))) {
return needsInstall('Installation path does not exist');
}
const installPathStats = yield sftp.lstat(pkg.installationPath);
if (!installPathStats.isDirectory()) {
return corrupt(
'Installation path already exists as a file',
'install-path-is-file');
} else if ((yield sftp.readdir(pkg.installationPath)).length === 0) {
return needsInstall('Installation path is empty');
} else if (!(yield sftp.exists(manifestFile))) {
return corrupt(
`Manifest does not exist at ${manifestFile}`,
'missing-manifest');
}
// The installation state we expect to see:
const manifest = (0, (_Manifest || _load_Manifest()).deserializeManifest)((yield sftp.readFile(manifestFile)));
_this._currentVersion = manifest.version;
// The actual state:
const currentFileTree = yield sftp.filetree(pkg.installationPath);
const current = (0, (_Manifest || _load_Manifest()).createManifest)(
manifest.version /** Assume current version = manifest version */,
manifestFile,
pkg.installationPath,
currentFileTree);
// Is the manifest correct?
const check = (0, (_Manifest || _load_Manifest()).compareManifests)(manifest, current);
if (
// Should be equivalent to `check.status !== 'okay'`, but Flow is not smart enough :(
check.status === 'changed-files' ||
check.status === 'diff-versions' // This case should not happen
) {
return corrupt(check.message, check.status);
}
// The manifest is correct!
// Do we need to upgrade the package?
const expectedVersion = yield pkg.expectedVersion(manifest.version);
if (expectedVersion !== manifest.version) {
return needsUpdate(expectedVersion, manifest.version);
}
return okay();
} finally {
yield sftp.end();
}})();
async _uninstall(sftp) {
(0, _log4js().getLogger)().info('Removing previous package (if present)...');
await sftp.rmtree(this._package.installationPath, true);
}
/**
* Ensure that the installation path exists and is empty (if `clean`).
*/
install(ssh, options) {var _this2 = this;return (0, _asyncToGenerator.default)(function* () {
const pkg = _this2._package;
let sftp = yield ssh.sftp();
const opts = Object.assign({}, options);
opts.name = opts.name !== undefined ? opts.name : 'package';
opts.tempDir = opts.tempDir != null ? opts.tempDir : '/tmp/';
opts.force = opts.force || false;
try {
(0, (_log4js || _load_log4js()).getLogger)().info(`Installing ${opts.name}...`);
const archive = yield pkg.package(_this2._currentVersion);
const cleanFirst = archive.isDelta !== true;
async _initInstallationPath(sftp, params) {
const assertEmpty = params.assertEmpty;
const pkg = this._package;
// If we're not installing a delta and we believe there are existing files that need to be
// removed (e.g. we're updating the installation), then first remove the existing files.
if (cleanFirst && opts.force) {
yield _this2._uninstall(sftp);
_this2._currentVersion = null;
}
const installPathIsNonEmpty = () => sftp.readdir(pkg.installationPath).then(files => files.length !== 0, error => false);
yield _this2._initInstallationPath(sftp, { assertEmpty: cleanFirst });
if (assertEmpty && ( // We should have checked the manifest to prevent this from happening, but just in case...
// (Also note that we do not have transactional access to the server filesystem, so
// this can happen and we might not even catch it here.)
await installPathIsNonEmpty())) {
throw new InstallError(`Installation path is not empty (${pkg.installationPath})`);
}
if (pkg.extract) {
const extract = pkg.extract;
// Transfer the archive to a file first? Otherwise, it will be streamed to the extractor.
if (extract.fromFileCommand) {
const tmp = _path.default.join(
opts.tempDir,
`big-dig-package.${Math.random()}`);
let dir;
(0, (_log4js || _load_log4js()).getLogger)().info(
`Install: transferring package to temporary file (${tmp})...`);
if (pkg.extract) {
dir = pkg.installationPath;
} else {
dir = _path.default.dirname(pkg.installationPath);
}
yield _this2._transferToFile(archive, tmp, sftp);
yield sftp.end();
try {
(0, (_log4js || _load_log4js()).getLogger)().info('Install: extracting package...');
yield _this2._extractFromFile(extract, tmp, ssh);
} finally {
try {
sftp = yield ssh.sftp();
yield sftp.unlink(tmp);
yield sftp.end();
} catch (error) {
(0, (_log4js || _load_log4js()).getLogger)().warn(
`Install: could not remove temporary file ${tmp}.`);
}
}
} else {
yield sftp.end();
(0, (_log4js || _load_log4js()).getLogger)().info('Install: transferring and extracting package...');
yield _this2._extractFromStdin(archive, extract, ssh);
}
sftp = yield ssh.sftp();
} else {
(0, (_log4js || _load_log4js()).getLogger)().info('Install: transferring package...');
yield _this2._transferToFile(archive, pkg.installationPath, sftp);
}
(0, (_log4js || _load_log4js()).getLogger)().info('Install: saving manifest...');
yield _this2._saveManifest(archive.version, sftp);
_this2._currentVersion = archive.version;
(0, (_log4js || _load_log4js()).getLogger)().info('Install: complete.');
} catch (error) {
(0, (_log4js || _load_log4js()).getLogger)().info(
`Install: FAILURE (${error.message || error.toString()})`);
throw error;
} finally {
yield sftp.end();
}})();
await sftp.mkdir(dir, {
createIntermediateDirectories: true
});
}
/**
* Returns the remote path of the package.
*/
getInstallationPath() {
return this._package.installationPath;
}
* Extract a local file/buffer to the remote install path via stdin.
*/
/**
* Assuming that the package files have all been installed, this creates and saves a manifest of
* the state of the installation.
*/
_saveManifest(version, sftp) {var _this3 = this;return (0, _asyncToGenerator.default)(function* () {
const installPath = _this3._package.installationPath;
const currentFileTree = yield sftp.filetree(installPath);
const current = (0, (_Manifest || _load_Manifest()).createManifest)(
version,
_this3._manifestPath(),
installPath,
currentFileTree);
const data = (0, (_Manifest || _load_Manifest()).serializeManifest)(current);
yield sftp.writeFile(_this3._manifestPath(), data);})();
}
async _extractFromStdin(archive, extract, ssh) {
const pkg = this._package;
const extractCmd = extract.fromStdinCommand(pkg.installationPath);
const {
stdout,
stdio,
result
} = await ssh.exec(extractCmd);
let output = '';
stdout.subscribe(data => output += data);
const [, {
code
}] = await Promise.all([this._transferViaStream(archive, stdio), result]);
/**
* Removes all (preexisting) files from the installation path.
*/
_uninstall(sftp) {var _this4 = this;return (0, _asyncToGenerator.default)(function* () {
(0, (_log4js || _load_log4js()).getLogger)().info('Removing previous package (if present)...');
yield sftp.rmtree(_this4._package.installationPath, true);})();
if (code !== 0) {
const codeStr = code == null ? '<null>' : code;
throw new InstallError(`Extraction command exited with code ${codeStr}: ${extractCmd}`, output);
}
}
/**
* Ensure that the installation path exists and is empty (if `clean`).
*/
_initInstallationPath(
sftp,
params)
{var _this5 = this;return (0, _asyncToGenerator.default)(function* () {
const assertEmpty = params.assertEmpty;
const pkg = _this5._package;
const installPathIsNonEmpty = function () {return (
sftp.
readdir(pkg.installationPath).
then(function (files) {return files.length !== 0;}, function (error) {return false;}));};
* Extract a remote file to a remote directory.
*/
if (
assertEmpty && (
// We should have checked the manifest to prevent this from happening, but just in case...
// (Also note that we do not have transactional access to the server filesystem, so
// this can happen and we might not even catch it here.)
yield installPathIsNonEmpty()))
{
throw new InstallError(
`Installation path is not empty (${pkg.installationPath})`);
}
async _extractFromFile(extract, archiveFile, ssh) {
const pkg = this._package;
const extractCmd = extract.fromFileCommand(archiveFile, pkg.installationPath);
const {
stdout,
result
} = await ssh.exec(extractCmd);
let output = '';
stdout.subscribe(data => output += data);
const {
code
} = await result;
let dir;
if (pkg.extract) {
dir = pkg.installationPath;
} else {
dir = _path.default.dirname(pkg.installationPath);
}
yield sftp.mkdir(dir, { createIntermediateDirectories: true });})();
if (code !== 0) {
const codeStr = code == null ? '<null>' : code;
throw new InstallError(`Extraction command exited with code ${codeStr}: ${extractCmd}`, output);
}
}
/**
* Extract a local file/buffer to the remote install path via stdin.
*/
_extractFromStdin(
archive,
extract,
ssh)
{var _this6 = this;return (0, _asyncToGenerator.default)(function* () {
const pkg = _this6._package;
const extractCmd = extract.fromStdinCommand(pkg.installationPath);
const { stdout, stdio, result } = yield ssh.exec(extractCmd);
let output = '';
stdout.subscribe(function (data) {return output += data;});
const [, { code }] = yield Promise.all([
_this6._transferViaStream(archive, stdio),
result]);
* Transfer the local file/buffer to a remote file.
*/
if (code !== 0) {
const codeStr = code == null ? '<null>' : code;
throw new InstallError(
`Extraction command exited with code ${codeStr}: ${extractCmd}`,
output);
}})();
async _transferToFile(archive, archiveFile, sftp) {
if (archive.data) {
await sftp.writeFile(archiveFile, archive.data, {
flag: 'wx'
});
} else {
await sftp.fastPut(archive.filename, archiveFile);
}
}
/**
* Extract a remote file to a remote directory.
*/
_extractFromFile(
extract,
archiveFile,
ssh)
{var _this7 = this;return (0, _asyncToGenerator.default)(function* () {
const pkg = _this7._package;
const extractCmd = extract.fromFileCommand(
archiveFile,
pkg.installationPath);
* Transfer the local file/buffer to a stream (e.g. combined with `_extractFromStdin`).
*/
const { stdout, result } = yield ssh.exec(extractCmd);
let output = '';
stdout.subscribe(function (data) {return output += data;});
const { code } = yield result;
if (code !== 0) {
const codeStr = code == null ? '<null>' : code;
throw new InstallError(
`Extraction command exited with code ${codeStr}: ${extractCmd}`,
output);
}})();
}
/**
* Transfer the local file/buffer to a remote file.
*/
_transferToFile(
archive,
archiveFile,
sftp)
{return (0, _asyncToGenerator.default)(function* () {
if (archive.data) {
yield sftp.writeFile(archiveFile, archive.data, { flag: 'wx' });
} else {
yield sftp.fastPut(archive.filename, archiveFile);
}})();
}
/**
* Transfer the local file/buffer to a stream (e.g. combined with `_extractFromStdin`).
*/
_transferViaStream(
archive,
stream)
{
_transferViaStream(archive, stream) {
if (archive.data) {

@@ -586,10 +476,13 @@ stream.write(archive.data);

}
return (0, (_events || _load_events()).onceEvent)(stream, 'end');
return (0, _events().onceEvent)(stream, 'end');
}
/**
* Remote filename for the manifest.
*/
/**
* Remote filename for the manifest.
*/
_manifestPath() {
const pkg = this._package;
if (pkg.extract) {

@@ -600,7 +493,6 @@ // Store the manifest within the installation directory

// Store the manifest as a sibling of the installed file
return _path.default.join(
_path.default.dirname(this._package.installationPath),
_path.default.basename('.big-dig-manifest' + pkg.installationPath));
return _path.default.join(_path.default.dirname(this._package.installationPath), _path.default.basename('.big-dig-manifest' + pkg.installationPath));
}
}
}
}}
}

@@ -1,390 +0,385 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.SftpClient = exports.FileEntry = undefined;var _asyncToGenerator = _interopRequireDefault(require('async-to-generator'));var _ssh;
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SftpClient = exports.FileEntry = void 0;
function _ssh() {
const data = require("ssh2");
_ssh = function () {
return data;
};
return data;
}
var _RxMin = require("rxjs/bundles/Rx.min.js");
var pathModule = _interopRequireWildcard(require("path"));
var fs = _interopRequireWildcard(require("fs"));
function _promise() {
const data = require("nuclide-commons/promise");
_promise = function () {
return data;
};
return data;
}
function _load_ssh() {return _ssh = require('ssh2');}
var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js');
var _path = _interopRequireWildcard(require('path'));
var _fs = _interopRequireWildcard(require('fs'));var _promise;
function _load_promise() {return _promise = require('nuclide-commons/promise');}function _interopRequireWildcard(obj) {if (obj && obj.__esModule) {return obj;} else {var newObj = {};if (obj != null) {for (var key in obj) {if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key];}}newObj.default = obj;return newObj;}}function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
/**
* This is essentially FileEntry from ssh2, but provides `Stats` instead of just `Attributes`.
* (I.e. adds helper functions to query the stats.)
*/ /** `readFile` returns either a string, if an encoding was specified, or else Buffer */
* This is essentially FileEntry from ssh2, but provides `Stats` instead of just `Attributes`.
* (I.e. adds helper functions to query the stats.)
*/
class FileEntry {
constructor(entry) {
this.filename = entry.filename;
this.longname = entry.longname;
this.stats = Object.assign({},
entry.attrs, {
this.stats = Object.assign({}, entry.attrs, {
_checkModeProperty(property) {
// eslint-disable-next-line no-bitwise
return (this.mode & _fs.constants.S_IFMT) === property;
return (this.mode & fs.constants.S_IFMT) === property;
},
isDirectory() {
return this._checkModeProperty(_fs.constants.S_IFDIR);
return this._checkModeProperty(fs.constants.S_IFDIR);
},
isFile() {
return this._checkModeProperty(_fs.constants.S_IFREG);
return this._checkModeProperty(fs.constants.S_IFREG);
},
isBlockDevice() {
return this._checkModeProperty(_fs.constants.S_IFBLK);
return this._checkModeProperty(fs.constants.S_IFBLK);
},
isCharacterDevice() {
return this._checkModeProperty(_fs.constants.S_IFCHR);
return this._checkModeProperty(fs.constants.S_IFCHR);
},
isSymbolicLink() {
return this._checkModeProperty(_fs.constants.S_IFLNK);
return this._checkModeProperty(fs.constants.S_IFLNK);
},
isFIFO() {
return this._checkModeProperty(_fs.constants.S_IFIFO);
return this._checkModeProperty(fs.constants.S_IFIFO);
},
isSocket() {
return this._checkModeProperty(_fs.constants.S_IFSOCK);
} });
return this._checkModeProperty(fs.constants.S_IFSOCK);
}
}}exports.FileEntry = FileEntry;
});
}
}
/**
* Represents an SFTP connection. This wraps the `SFTPWrapper` class from ssh2, but reinterprets the
* API using promises instead of callbacks. The methods of this class generally correspond to the
* same methods on `SFTPWrapper`. Instances of this class should typically be obtained from
* `SshClient`.
*/ /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/class SftpClient {
/**
* Wraps and takes ownership of the `SFTPWrapper`.
*/
constructor(sftp) {this._deferredContinue = null;this.
* Represents an SFTP connection. This wraps the `SFTPWrapper` class from ssh2, but reinterprets the
* API using promises instead of callbacks. The methods of this class generally correspond to the
* same methods on `SFTPWrapper`. Instances of this class should typically be obtained from
* `SshClient`.
*/
exports.FileEntry = FileEntry;
class SftpClient {
/**
* Wraps and takes ownership of the `SFTPWrapper`.
*/
constructor(sftp) {
this._deferredContinue = null;
this.readFile = function (path, ...args) {
return this._sftpToPromise(this._sftp.readFile, path, ...args);
};
this._sftp = sftp;
this._onError = _RxMin.Observable.fromEvent(this._sftp, 'error');
this._onEnd = _RxMin.Observable.fromEvent(this._sftp, 'end');
this._onClose = _RxMin.Observable.fromEvent(this._sftp, 'close');
this._onContinue = _RxMin.Observable.fromEvent(this._sftp, 'continue');
this._closePromise = new (_promise().Deferred)();
this._endPromise = new (_promise().Deferred)();
this._sftp.on('end', this._endPromise.resolve);
this._sftp.on('continue', () => this._resolveContinue());
this._sftp.on('close', () => {
this._resolveContinue();
this._endPromise.resolve();
this._closePromise.resolve();
});
}
/**
* @return `true` if the channel is ready for more data; `false` if the caller should wait for
* the 'continue' event before sending more data. This variable is updated immediately after each
* asynchronous call (i.e. when a Promise is returned; before it is necessarily resolved).
*/
get continue() {
return this._deferredContinue == null;
}
/** Emitted when an error occurred. */
onError() {
return this._onError;
}
/** Emitted when the session has ended. */
onEnd() {
return this._onEnd;
}
/** Emitted when the session has closed. */
onClose() {
return this._onClose;
}
/** Emitted when more requests/data can be sent to the stream. */
onContinue() {
return this._onContinue;
}
/**
* Downloads a file at `remotePath` to `localPath` using parallel reads for faster throughput.
*/
fastGet(remotePath, localPath, options = {}) {
return this._sftpToPromise(this._sftp.fastGet, remotePath, localPath, options);
}
/**
* Uploads a file from `localPath` to `remotePath` using parallel reads for faster throughput.
*/
fastPut(localPath, remotePath, options = {}) {
return this._sftpToPromise(this._sftp.fastPut, localPath, remotePath, options);
}
/**
* Reads a file
* @param options either the encoding (string) or a bag of options
*/
readFile = function (path, ...args) {
return this._sftpToPromise(this._sftp.readFile, path, ...args);
};this._sftp = sftp;this._onError = _rxjsBundlesRxMinJs.Observable.fromEvent(this._sftp, 'error');this._onEnd = _rxjsBundlesRxMinJs.Observable.fromEvent(this._sftp, 'end');this._onClose = _rxjsBundlesRxMinJs.Observable.fromEvent(this._sftp, 'close');this._onContinue = _rxjsBundlesRxMinJs.Observable.fromEvent(this._sftp, 'continue');this._closePromise = new (_promise || _load_promise()).Deferred();this._endPromise = new (_promise || _load_promise()).Deferred();this._sftp.on('end', this._endPromise.resolve);this._sftp.on('continue', () => this._resolveContinue());this._sftp.on('close', () => {this._resolveContinue();this._endPromise.resolve();this._closePromise.resolve();});} /**
* @return `true` if the channel is ready for more data; `false` if the caller should wait for
* the 'continue' event before sending more data. This variable is updated immediately after each
* asynchronous call (i.e. when a Promise is returned; before it is necessarily resolved).
*/get continue() {return this._deferredContinue == null;} /** Emitted when an error occurred. */onError() {return this._onError;} /** Emitted when the session has ended. */onEnd() {return this._onEnd;} /** Emitted when the session has closed. */onClose() {return this._onClose;} /** Emitted when more requests/data can be sent to the stream. */onContinue() {return this._onContinue;} /**
* Downloads a file at `remotePath` to `localPath` using parallel reads for faster throughput.
*/fastGet(remotePath, localPath, options = {}) {return this._sftpToPromise(this._sftp.fastGet, remotePath, localPath, options);} /**
* Uploads a file from `localPath` to `remotePath` using parallel reads for faster throughput.
*/fastPut(localPath, remotePath, options = {}) {return this._sftpToPromise(this._sftp.fastPut, localPath, remotePath, options);} /**
* Reads a file
* @param options either the encoding (string) or a bag of options
*/ /**
* Writes to a file
* @param options either the encoding (string) or a bag of options
*/writeFile(path, data, options = { encoding: 'utf8', mode: 0o666, flag: 'w' }) {
/**
* Writes to a file
* @param options either the encoding (string) or a bag of options
*/
writeFile(path, data, options = {
encoding: 'utf8',
mode: 0o666,
flag: 'w'
}) {
return this._sftpToPromise(this._sftp.writeFile, path, data, options);
}
/**
* Retrieves attributes for `path`.
*
* Updates 'continue'.
*/
/**
* Retrieves attributes for `path`.
*
* Updates 'continue'.
*/
stat(path) {
return this._sftpToPromiseContinue(this._sftp.stat, path);
}
/**
* Retrieves attributes for `path`. If `path` is a symlink, the link itself is stat'ed
* instead of the resource it refers to.
*
* Updates 'continue'.
*/
/**
* Retrieves attributes for `path`. If `path` is a symlink, the link itself is stat'ed
* instead of the resource it refers to.
*
* Updates 'continue'.
*/
lstat(path) {
return this._sftpToPromiseContinue(this._sftp.lstat, path);
}
/**
* Returns `true` iff `path` is an existing file or directory.
*
* Updates 'continue'.
*/
async exists(path) {
// Note: SFTPWrapper and ssh2-streams include an `exists` method, which also uses `stat`.
// We reimplement it here so we can properly handle the `continue` event.
try {
await this.stat(path);
return true;
} catch (error) {
return false;
}
}
/**
* Returns `true` iff `path` is an existing file or directory.
*
* Updates 'continue'.
*/
exists(path) {var _this = this;return (0, _asyncToGenerator.default)(function* () {
// Note: SFTPWrapper and ssh2-streams include an `exists` method, which also uses `stat`.
// We reimplement it here so we can properly handle the `continue` event.
try {
yield _this.stat(path);
return true;
} catch (error) {
return false;
}})();
}
* Retrieves a directory listing.
*
* Updates 'continue'.
*/
/**
* Retrieves a directory listing.
*
* Updates 'continue'.
*/
readdir(location) {
return this._sftpToPromiseContinue(this._sftp.readdir, location).then(
files =>
files.map(entry => new FileEntry(entry)));
return this._sftpToPromiseContinue(this._sftp.readdir, location).then(files => files.map(entry => new FileEntry(entry)));
}
/**
* Removes the directory at `path`.
*
* Updates `continue`.
*/
/**
* Removes the directory at `path`.
*
* Updates `continue`.
*/
rmdir(path) {
return this._sftpToPromiseContinue(this._sftp.rmdir, path);
}
/**
* Removes the file/symlink at `path`.
*
* Updates `continue`.
*/
/**
* Removes the file/symlink at `path`.
*
* Updates `continue`.
*/
unlink(path) {
return this._sftpToPromiseContinue(this._sftp.unlink, path);
}
/**
* Updates `continue`.
*/
/**
* Updates `continue`.
*/
filetree(
path)
{var _this2 = this;return (0, _asyncToGenerator.default)(function* () {
const stats = yield _this2.lstat(path);
const results = [{ filename: path, stats }];
if (stats.isDirectory()) {
results.push(...(yield _this2._dirtree(path)));
}
return results;})();
async filetree(path) {
const stats = await this.lstat(path);
const results = [{
filename: path,
stats
}];
if (stats.isDirectory()) {
results.push(...(await this._dirtree(path)));
}
return results;
}
_dirtree(
path)
{var _this3 = this;return (0, _asyncToGenerator.default)(function* () {
const files = yield _this3.readdir(path);
const results = [];
yield Promise.all(
files.map((() => {var _ref = (0, _asyncToGenerator.default)(function* (file) {
const filename = _path.join(path, file.filename);
results.push({ filename, stats: file.stats });
if (file.stats.isDirectory()) {
results.push(...(yield _this3._dirtree(filename)));
}
});return function (_x) {return _ref.apply(this, arguments);};})()));
async _dirtree(path) {
const files = await this.readdir(path);
const results = [];
await Promise.all(files.map(async file => {
const filename = pathModule.join(path, file.filename);
results.push({
filename,
stats: file.stats
});
return results;})();
if (file.stats.isDirectory()) {
results.push(...(await this._dirtree(filename)));
}
}));
return results;
}
/**
* Deletes an entire directory tree or file. If this operation fails, a subset of files may
* have been deleted.
* Updates `continue`.
* @param path the directory to remove
* @param ignoreErrors silently return if an error is encountered.
* @return `true` if successful; `false` if `ignoreErrors==true` and unsuccessful
*/
rmtree(path, ignoreErrors = false) {var _this4 = this;return (0, _asyncToGenerator.default)(function* () {
try {
const stat = yield _this4.lstat(path);
if (stat.isDirectory()) {
yield _this4._rmtree(path);
} else {
yield _this4.unlink(path);
}
return true;
} catch (error) {
if (ignoreErrors) {
return false;
} else {
throw error;
}
}})();
}
* Deletes an entire directory tree or file. If this operation fails, a subset of files may
* have been deleted.
* Updates `continue`.
* @param path the directory to remove
* @param ignoreErrors silently return if an error is encountered.
* @return `true` if successful; `false` if `ignoreErrors==true` and unsuccessful
*/
_rmtree(path) {var _this5 = this;return (0, _asyncToGenerator.default)(function* () {
const files = yield _this5.readdir(path);
yield Promise.all(
files.map((() => {var _ref2 = (0, _asyncToGenerator.default)(function* (file) {
const filename = _path.join(path, file.filename);
if (file.stats.isDirectory()) {
yield _this5._rmtree(filename);
} else {
yield _this5.unlink(filename);
}
});return function (_x2) {return _ref2.apply(this, arguments);};})()));
async rmtree(path, ignoreErrors = false) {
try {
const stat = await this.lstat(path);
yield _this5.rmdir(path);})();
if (stat.isDirectory()) {
await this._rmtree(path);
} else {
await this.unlink(path);
}
return true;
} catch (error) {
if (ignoreErrors) {
return false;
} else {
throw error;
}
}
}
/**
* Creates a new directory `path`.
*
* Updates 'continue'.
* @param createIntermediateDirectories Same as the -p option to Posix mkdir. Creates any
* intermediate directories as required.
*/
mkdir(
path,
attributes =
async _rmtree(path) {
const files = await this.readdir(path);
await Promise.all(files.map(async file => {
const filename = pathModule.join(path, file.filename);
{})
{var _this6 = this;return (0, _asyncToGenerator.default)(function* () {
if (attributes.createIntermediateDirectories) {
return _this6._mkdirCreateIntermediate(path, attributes);
if (file.stats.isDirectory()) {
await this._rmtree(filename);
} else {
return _this6._sftpToPromiseContinue(_this6._sftp.mkdir, path, attributes);
}})();
await this.unlink(filename);
}
}));
await this.rmdir(path);
}
/**
* Creates a new directory `path`.
*
* Updates 'continue'.
* @param createIntermediateDirectories Same as the -p option to Posix mkdir. Creates any
* intermediate directories as required.
*/
_mkdirCreateIntermediate(
path,
attributes = {})
{var _this7 = this;return (0, _asyncToGenerator.default)(function* () {
if (yield _this7.exists(path)) {
return;
}
const parent = _path.dirname(path);
yield _this7._mkdirCreateIntermediate(parent, attributes);
return _this7._sftpToPromiseContinue(_this7._sftp.mkdir, path, attributes);})();
async mkdir(path, attributes = {}) {
if (attributes.createIntermediateDirectories) {
return this._mkdirCreateIntermediate(path, attributes);
} else {
return this._sftpToPromiseContinue(this._sftp.mkdir, path, attributes);
}
}
async _mkdirCreateIntermediate(path, attributes = {}) {
if (await this.exists(path)) {
return;
}
const parent = pathModule.dirname(path);
await this._mkdirCreateIntermediate(parent, attributes);
return this._sftpToPromiseContinue(this._sftp.mkdir, path, attributes);
}
/**
* Ends the stream.
*/
end() {var _this8 = this;return (0, _asyncToGenerator.default)(function* () {
yield _this8._readyForData();
_this8._sftp.end();
return _this8._endPromise.promise;})();
* Ends the stream.
*/
async end() {
await this._readyForData();
this._sftp.end();
return this._endPromise.promise;
}

@@ -394,3 +389,5 @@

if (this._deferredContinue != null) {
const { resolve } = this._deferredContinue;
const {
resolve
} = this._deferredContinue;
this._deferredContinue = null;

@@ -401,37 +398,43 @@ resolve();

_readyForData() {var _this9 = this;return (0, _asyncToGenerator.default)(function* () {
while (_this9._deferredContinue != null) {
// eslint-disable-next-line no-await-in-loop
yield _this9._deferredContinue.promise;
}})();
async _readyForData() {
while (this._deferredContinue != null) {
// eslint-disable-next-line no-await-in-loop
await this._deferredContinue.promise;
}
}
_sftpToPromiseContinue(func, ...args) {var _this10 = this;return (0, _asyncToGenerator.default)(function* () {
yield _this10._readyForData();
return new Promise(function (resolve, reject) {
args.push(function (err, result) {
if (err != null) {
return reject(err);
}
resolve(result);
});
async _sftpToPromiseContinue(func, ...args) {
await this._readyForData();
return new Promise((resolve, reject) => {
args.push((err, result) => {
if (err != null) {
return reject(err);
}
const readyForData = func.apply(_this10._sftp, args);
if (!readyForData && _this10._deferredContinue == null) {
_this10._deferredContinue = new (_promise || _load_promise()).Deferred();
resolve(result);
});
const readyForData = func.apply(this._sftp, args);
if (!readyForData && this._deferredContinue == null) {
this._deferredContinue = new (_promise().Deferred)();
}
});
}
async _sftpToPromise(func, ...args) {
await this._readyForData();
return new Promise((resolve, reject) => {
args.push((err, result) => {
if (err != null) {
return reject(err);
}
});})();
resolve(result);
});
func.apply(this._sftp, args);
});
}
_sftpToPromise(func, ...args) {var _this11 = this;return (0, _asyncToGenerator.default)(function* () {
yield _this11._readyForData();
return new Promise(function (resolve, reject) {
args.push(function (err, result) {
if (err != null) {
return reject(err);
}
resolve(result);
});
func.apply(_this11._sftp, args);
});})();
}}exports.SftpClient = SftpClient;
}
exports.SftpClient = SftpClient;

@@ -1,189 +0,196 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.SshClient = exports.SshClosedError = undefined;var _asyncToGenerator = _interopRequireDefault(require('async-to-generator'));var _promise;
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SshClient = exports.SshClosedError = void 0;
function _promise() {
const data = require("nuclide-commons/promise");
_promise = function () {
return data;
};
return data;
}
function _stream() {
const data = require("nuclide-commons/stream");
_stream = function () {
return data;
};
return data;
}
function _ssh() {
const data = require("ssh2");
_ssh = function () {
return data;
};
return data;
}
var _RxMin = require("rxjs/bundles/Rx.min.js");
function _SftpClient() {
const data = require("./SftpClient");
_SftpClient = function () {
return data;
};
return data;
}
function _events() {
const data = require("../common/events");
_events = function () {
return data;
};
return data;
}
function _load_promise() {return _promise = require('nuclide-commons/promise');}var _stream;
function _load_stream() {return _stream = require('nuclide-commons/stream');}var _ssh;
function _load_ssh() {return _ssh = require('ssh2');}
var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js');var _SftpClient;
function _load_SftpClient() {return _SftpClient = require('./SftpClient');}var _events;
function _load_events() {return _events = require('../common/events');}function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
const OPEN_CHANNEL_ATTEMPTS = 3;
const OPEN_CHANNEL_DELAY_MS = 200;
/**
* Emitted when the server is asking for replies to the given `prompts` for keyboard-
* interactive user authentication.
*
* * `name` is generally what you'd use as a window title (for GUI apps).
* * `prompts` is an array of `Prompt` objects.
*
* The answers for all prompts must be returned as an array of strings in the same order.
*
* NOTE: It's possible for the server to come back and ask more questions.
*/
const OPEN_CHANNEL_ATTEMPTS = 3; /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/const OPEN_CHANNEL_DELAY_MS = 200; /**
* Emitted when the server is asking for replies to the given `prompts` for keyboard-
* interactive user authentication.
*
* * `name` is generally what you'd use as a window title (for GUI apps).
* * `prompts` is an array of `Prompt` objects.
*
* The answers for all prompts must be returned as an array of strings in the same order.
*
* NOTE: It's possible for the server to come back and ask more questions.
*/
class SshClosedError extends Error {
constructor(message) {
super(message);
}}exports.SshClosedError = SshClosedError;
}
}
exports.SshClosedError = SshClosedError;
/**
* Represents an SSH connection. This wraps the `Client` class from ssh2, but reinterprets the
* API using promises instead of callbacks. The methods of this class generally correspond to the
* same methods on `Client`.
*/
* Represents an SSH connection. This wraps the `Client` class from ssh2, but reinterprets the
* API using promises instead of callbacks. The methods of this class generally correspond to the
* same methods on `Client`.
*/
class SshClient {
/**
* Wraps and takes ownership of the ssh2 Client.
* @param {*} client
* @param {*} onKeyboard - a callback to provide interactive prompts to the user
*/
constructor(client, onKeyboard) {this._deferredContinue = null;
this._client = client || new (_ssh || _load_ssh()).Client();
this._onError = _rxjsBundlesRxMinJs.Observable.fromEvent(this._client, 'error');
this._onClose = _rxjsBundlesRxMinJs.Observable.fromEvent(
this._client,
'close',
hadError => ({
hadError }));
* Wraps and takes ownership of the ssh2 Client.
* @param {*} client
* @param {*} onKeyboard - a callback to provide interactive prompts to the user
*/
constructor(client, onKeyboard) {
this._deferredContinue = null;
this._client = client || new (_ssh().Client)();
this._onError = _RxMin.Observable.fromEvent(this._client, 'error');
this._onClose = _RxMin.Observable.fromEvent(this._client, 'close', hadError => ({
hadError
}));
this._closePromise = new (_promise().Deferred)();
this._endPromise = new (_promise().Deferred)();
this._client.on('end', this._endPromise.resolve);
this._closePromise = new (_promise || _load_promise()).Deferred();
this._endPromise = new (_promise || _load_promise()).Deferred();
this._client.on('continue', () => this._resolveContinue());
this._client.on('end', this._endPromise.resolve);
this._client.on('continue', () => this._resolveContinue());
this._client.on('close', hadError => {
this._resolveContinue();
this._endPromise.resolve();
this._closePromise.resolve({ hadError });
this._closePromise.resolve({
hadError
});
});
this._client.on(
'keyboard-interactive',
(
name,
instructions,
lang,
prompts,
finish) =>
onKeyboard(name, instructions, lang, prompts).then(finish));
this._client.on('keyboard-interactive', (name, instructions, lang, prompts, finish) => onKeyboard(name, instructions, lang, prompts).then(finish));
}
/**
* Emitted when an error occurred.
*/
/**
* Emitted when an error occurred.
*/
onError() {
return this._onError;
}
/**
* Emitted when the socket was closed.
*/
/**
* Emitted when the socket was closed.
*/
onClose() {
return this._onClose;
}
/**
* @return `true` if the channel is ready for more data; `false` if the caller should wait for
* the 'continue' event before sending more data. This variable is updated immediately after each
* asynchronous call (i.e. when a Promise is returned; before it is necessarily resolved).
*/
/**
* @return `true` if the channel is ready for more data; `false` if the caller should wait for
* the 'continue' event before sending more data. This variable is updated immediately after each
* asynchronous call (i.e. when a Promise is returned; before it is necessarily resolved).
*/
continue() {
return this._deferredContinue == null;
}
/**
* Attempts a connection to a server.
*
* @throws `Error & ClientErrorExtensions` if the connection failed
*/
/**
* Attempts a connection to a server.
*
* @throws `Error & ClientErrorExtensions` if the connection failed
*/
connect(config) {
const { promise, resolve, reject } = new (_promise || _load_promise()).Deferred();
const {
promise,
resolve,
reject
} = new (_promise().Deferred)();
function onClose() {
reject(new SshClosedError('Connection closed before completion'));
}
this._client.
once('ready', resolve).
once('close', onClose).
once('error', reject);
this._client.once('ready', resolve).once('close', onClose).once('error', reject);
this._client.connect(config);
return (0, (_promise || _load_promise()).lastly)(promise, () => {
this._client.
removeListener('ready', resolve).
removeListener('close', onClose).
removeListener('error', reject);
return (0, _promise().lastly)(promise, () => {
this._client.removeListener('ready', resolve).removeListener('close', onClose).removeListener('error', reject);
});
}
/**
* Executes a command on the server.
*
* @param command The command to execute.
* @param options Options for the command.
*/
exec(command, options = {}) {var _this = this;return (0, _asyncToGenerator.default)(function* () {
const stdio = yield _this._clientToPromiseContinue(
_this._client.exec,
command,
options);
* Executes a command on the server.
*
* @param command The command to execute.
* @param options Options for the command.
*/
return {
stdio,
result: (0, (_events || _load_events()).onceEvent)(stdio, 'close').then(
function (
async exec(command, options = {}) {
const stdio = await this._clientToPromiseContinue(this._client.exec, command, options);
return {
stdio,
result: (0, _events().onceEvent)(stdio, 'close').then((code, signal, dump, description, language) => ({
code,

@@ -193,60 +200,53 @@ signal,

description,
language) {return (
{ code, signal, dump, description, language });}),
language
})),
stdout: (0, _stream().observeStream)(stdio)
};
}
/**
* Open a connection with `srcIP` and `srcPort` as the originating address and port and
* `dstIP` and `dstPort` as the remote destination address and port.
*
* Updates 'continue'
*
* @param srcIP The originating address.
* @param srcPort The originating port.
* @param dstIP The destination address.
* @param dstPort The destination port.
*/
stdout: (0, (_stream || _load_stream()).observeStream)(stdio) };})();
forwardOut(srcIP, srcPort, dstIP, dstPort) {
return this._clientToPromiseContinue(this._client.forwardOut, srcIP, srcPort, dstIP, dstPort);
}
/**
* Open a connection with `srcIP` and `srcPort` as the originating address and port and
* `dstIP` and `dstPort` as the remote destination address and port.
*
* Updates 'continue'
*
* @param srcIP The originating address.
* @param srcPort The originating port.
* @param dstIP The destination address.
* @param dstPort The destination port.
*/
forwardOut(
srcIP,
srcPort,
dstIP,
dstPort)
{
return this._clientToPromiseContinue(
this._client.forwardOut,
srcIP,
srcPort,
dstIP,
dstPort);
* Starts an SFTP session.
*
* Updates 'continue'
*/
sftp(timeoutMs) {
return this._clientToPromiseContinue(this._client.sftp).then(sftp => new (_SftpClient().SftpClient)(sftp));
}
/**
* Starts an SFTP session.
*
* Updates 'continue'
*/
sftp(timeoutMs) {
return this._clientToPromiseContinue(this._client.sftp).then(
sftp => new (_SftpClient || _load_SftpClient()).SftpClient(sftp));
* Disconnects the socket.
*/
async end() {
await this._readyForData();
this._client.end();
return this._endPromise.promise;
}
/**
* Disconnects the socket.
*/
end() {var _this2 = this;return (0, _asyncToGenerator.default)(function* () {
yield _this2._readyForData();
_this2._client.end();
return _this2._endPromise.promise;})();
}
* Destroys the socket.
*/
/**
* Destroys the socket.
*/
destroy() {
this._client.destroy();
return this._closePromise.promise.then(() => {});

@@ -257,3 +257,5 @@ }

if (this._deferredContinue != null) {
const { resolve } = this._deferredContinue;
const {
resolve
} = this._deferredContinue;
this._deferredContinue = null;

@@ -264,7 +266,7 @@ resolve();

_readyForData() {var _this3 = this;return (0, _asyncToGenerator.default)(function* () {
while (_this3._deferredContinue != null) {
// eslint-disable-next-line no-await-in-loop
yield _this3._deferredContinue.promise;
}})();
async _readyForData() {
while (this._deferredContinue != null) {
// eslint-disable-next-line no-await-in-loop
await this._deferredContinue.promise;
}
}

@@ -276,10 +278,12 @@

let attempts = 0;
const self = this;
const self = this;
function doOperation() {
++attempts;
self._readyForData().then(() => {
const readyForData = func.apply(self._client, args);
if (!readyForData && this._deferredContinue == null) {
self._deferredContinue = new (_promise || _load_promise()).Deferred();
self._deferredContinue = new (_promise().Deferred)();
}

@@ -291,8 +295,3 @@ });

if (err != null) {
if (
err instanceof Error &&
err.message === '(SSH) Channel open failure: open failed' &&
err.reason === 'ADMINISTRATIVELY_PROHIBITED' &&
attempts < OPEN_CHANNEL_ATTEMPTS)
{
if (err instanceof Error && err.message === '(SSH) Channel open failure: open failed' && err.reason === 'ADMINISTRATIVELY_PROHIBITED' && attempts < OPEN_CHANNEL_ATTEMPTS) {
// In case we're severely limited in the number of channels available, we may have to

@@ -306,7 +305,11 @@ // wait a little while before the previous channel is closed. (If it was closed.)

}
resolve(result);
});
doOperation();
});
}}exports.SshClient = SshClient;
}
}
exports.SshClient = SshClient;

@@ -1,64 +0,75 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.default =
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = asyncRequest;
function _request() {
const data = _interopRequireDefault(require("request"));
_request = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
/**
* Promisified version of the request function:
* https://www.npmjs.com/package/request#requestoptions-callback
* Defaults to using the node's querystring module to encode the url query parameters.
* If you want to use the npm's qs module to encode the query parameters, explicitly provide
* the option:
* {useQuerystring: false}
*/
function asyncRequest(options) {
return new Promise((resolve, reject) => {
if (options.useQuerystring === undefined) {
options.useQuerystring = true;
} // TODO(t8118670): This can cause an uncaught exception.
// Likely requires a fix to 'request'.
(0, _request().default)(options, (error, response, body) => {
if (error) {
reject(error);
} else if (response.statusCode < 200 || response.statusCode >= 300) {
let errorJson = body;
if (typeof body !== 'object') {
try {
errorJson = JSON.parse(body);
} catch (e) {
// 404 responses aren't currently JSON.
errorJson = {
message: body
};
}
} // Cast to Object for use of code field below...
const err = new Error(errorJson.message); // Success http status codes range from 200 to 299.
asyncRequest;var _request;function _load_request() {return _request = _interopRequireDefault(require('request'));}function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };} /**
* Promisified version of the request function:
* https://www.npmjs.com/package/request#requestoptions-callback
* Defaults to using the node's querystring module to encode the url query parameters.
* If you want to use the npm's qs module to encode the query parameters, explicitly provide
* the option:
* {useQuerystring: false}
*/ /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/function asyncRequest(options) {return new Promise((resolve, reject) => {if (options.useQuerystring === undefined) {options.useQuerystring = true;} // TODO(t8118670): This can cause an uncaught exception.
// Likely requires a fix to 'request'.
(0, (_request || _load_request()).default)(options, (error, response, body) => {if (error) {reject(error);} else if (response.statusCode < 200 || response.statusCode >= 300) {let errorJson = body;if (typeof body !== 'object') {try {errorJson = JSON.parse(body);} catch (e) {// 404 responses aren't currently JSON.
errorJson = { message: body };}
}
// Cast to Object for use of code field below...
const err = new Error(errorJson.message);
// Success http status codes range from 200 to 299.
err.code = errorJson.code || response.statusCode;
reject(err);
} else {
resolve({ body, response });
resolve({
body,
response
});
}

@@ -65,0 +76,0 @@ });

@@ -1,47 +0,81 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.XhrConnectionHeartbeat = undefined;var _asyncToGenerator = _interopRequireDefault(require('async-to-generator'));var _asyncRequest;
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.XhrConnectionHeartbeat = void 0;
function _asyncRequest() {
const data = _interopRequireDefault(require("./utils/asyncRequest"));
_asyncRequest = function () {
return data;
};
return data;
}
function _eventKit() {
const data = require("event-kit");
_eventKit = function () {
return data;
};
return data;
}
function _log4js() {
const data = require("log4js");
_log4js = function () {
return data;
};
return data;
}
function _promise() {
const data = require("nuclide-commons/promise");
_promise = function () {
return data;
};
function _load_asyncRequest() {return _asyncRequest = _interopRequireDefault(require('./utils/asyncRequest'));}var _eventKit;
function _load_eventKit() {return _eventKit = require('event-kit');}var _log4js;
function _load_log4js() {return _log4js = require('log4js');}var _promise;
function _load_promise() {return _promise = require('nuclide-commons/promise');}function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };} /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/const HEARTBEAT_INTERVAL_MS = 10000;const HEARTBEAT_TIMEOUT_MS = 10000;const MAX_HEARTBEAT_AWAY_RECONNECT_MS = 60000;const CERT_NOT_YET_VALID_DELAY = 3000;const CERT_NOT_YET_VALID_RETRIES = 3;class XhrConnectionHeartbeat {
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* strict-local
* @format
*/
const HEARTBEAT_INTERVAL_MS = 10000;
const HEARTBEAT_TIMEOUT_MS = 10000;
const MAX_HEARTBEAT_AWAY_RECONNECT_MS = 60000;
const CERT_NOT_YET_VALID_DELAY = 3000;
const CERT_NOT_YET_VALID_RETRIES = 10;
const ECONNRESET_ERRORS_IN_ROW_LIMIT = 4;
constructor(
serverUri,
heartbeatChannel,
agentOptions)
{
class XhrConnectionHeartbeat {
constructor(serverUri, heartbeatChannel, agentOptions) {
this._heartbeatConnectedOnce = false;
this._lastHeartbeat = null;
this._lastHeartbeatTime = null;
this._connectionResetCount = 0;
const options = {
uri: `${serverUri}/${heartbeatChannel}`,
method: 'POST',
timeout: HEARTBEAT_TIMEOUT_MS };
timeout: HEARTBEAT_TIMEOUT_MS,
// We're trying this to see if it resolves T28442202
forever: true
};

@@ -51,4 +85,5 @@ if (agentOptions != null) {

}
this._options = options;
this._emitter = new (_eventKit || _load_eventKit()).Emitter();
this._emitter = new (_eventKit().Emitter)();

@@ -60,91 +95,123 @@ this._monitorServerHeartbeat();

this._heartbeat();
this._heartbeatInterval = setInterval(
() => this._heartbeat(),
HEARTBEAT_INTERVAL_MS);
this._heartbeatInterval = setInterval(() => this._heartbeat(), HEARTBEAT_INTERVAL_MS);
} // Returns version
async sendHeartBeat() {
let retries = CERT_NOT_YET_VALID_RETRIES;
while (true) {
try {
// eslint-disable-next-line no-await-in-loop
const {
body
} = await (0, _asyncRequest().default)(this._options);
return body;
} catch (err) {
if (retries-- > 0 && err.code === 'CERT_NOT_YET_VALID') {
(0, _log4js().getLogger)('XhrConnectionHeartbeat').warn(`Certificate not yet valid, retrying after ${CERT_NOT_YET_VALID_DELAY}ms...`); // eslint-disable-next-line no-await-in-loop
await (0, _promise().sleep)(CERT_NOT_YET_VALID_DELAY);
} else {
throw err;
}
}
} // eslint-disable-next-line no-unreachable
throw new Error('unreachable');
}
// Returns version
sendHeartBeat() {var _this = this;return (0, _asyncToGenerator.default)(function* () {
let retries = CERT_NOT_YET_VALID_RETRIES;
while (true) {
try {
// eslint-disable-next-line no-await-in-loop
const { body } = yield (0, (_asyncRequest || _load_asyncRequest()).default)(_this._options);
return body;
} catch (err) {
if (retries-- > 0 && err.code === 'CERT_NOT_YET_VALID') {
(0, (_log4js || _load_log4js()).getLogger)('XhrConnectionHeartbeat').warn(
`Certificate not yet valid, retrying after ${CERT_NOT_YET_VALID_DELAY}ms...`);
async _heartbeat() {
try {
await this.sendHeartBeat();
this._heartbeatConnectedOnce = true;
const now = Date.now(); // flowlint-next-line sketchy-null-number:off
// eslint-disable-next-line no-await-in-loop
yield (0, (_promise || _load_promise()).sleep)(CERT_NOT_YET_VALID_DELAY);
this._lastHeartbeatTime = this._lastHeartbeatTime || now;
if (this._lastHeartbeat === 'away' || now - this._lastHeartbeatTime > MAX_HEARTBEAT_AWAY_RECONNECT_MS) {
// Trigger a websocket reconnect.
this._emitter.emit('reconnect');
}
this._lastHeartbeat = 'here';
this._lastHeartbeatTime = now;
this._connectionResetCount = 0;
this._emitter.emit('heartbeat');
} catch (err) {
this._lastHeartbeat = 'away'; // Error code could could be one of:
// ['ENOTFOUND', 'ECONNREFUSED', 'ECONNRESET', 'ETIMEDOUT']
// A heuristic mapping is done between the xhr error code to the state of server connection.
const {
code: originalCode,
message
} = err;
let code = null;
switch (originalCode) {
case 'ENOTFOUND': // A socket operation failed because the network was down.
// falls through
case 'ENETDOWN': // The range of the temporary ports for connection are all taken,
// This is temporal with many http requests, but should be counted as a network away event.
// falls through
case 'EADDRNOTAVAIL': // The host server is unreachable, could be in a VPN.
// falls through
case 'EHOSTUNREACH': // A request timeout is considered a network away event.
// falls through
case 'ETIMEDOUT':
case 'ESOCKETTIMEDOUT':
code = 'NETWORK_AWAY';
break;
case 'ECONNREFUSED':
// Server shut down or port no longer accessible.
if (this._heartbeatConnectedOnce) {
code = 'SERVER_CRASHED';
} else {
throw err;
code = 'PORT_NOT_ACCESSIBLE';
}
}
break;
case 'ECONNRESET':
code = this._checkReconnectErrorType(originalCode);
break;
case 'CERT_SIGNATURE_FAILURE':
code = 'INVALID_CERTIFICATE';
break;
default:
code = originalCode;
break;
}
// eslint-disable-next-line no-unreachable
throw Error('unreachable');})();
if (code !== 'ECONNRESET') {
this._connectionResetCount = 0;
}
this._emitter.emit('heartbeat.error', {
code,
originalCode,
message
});
}
}
_heartbeat() {var _this2 = this;return (0, _asyncToGenerator.default)(function* () {
try {
yield _this2.sendHeartBeat();
_this2._heartbeatConnectedOnce = true;
const now = Date.now();
// flowlint-next-line sketchy-null-number:off
_this2._lastHeartbeatTime = _this2._lastHeartbeatTime || now;
if (
_this2._lastHeartbeat === 'away' ||
now - _this2._lastHeartbeatTime > MAX_HEARTBEAT_AWAY_RECONNECT_MS)
{
// Trigger a websocket reconnect.
_this2._emitter.emit('reconnect');
}
_this2._lastHeartbeat = 'here';
_this2._lastHeartbeatTime = now;
_this2._emitter.emit('heartbeat');
} catch (err) {
_this2._lastHeartbeat = 'away';
// Error code could could be one of:
// ['ENOTFOUND', 'ECONNREFUSED', 'ECONNRESET', 'ETIMEDOUT']
// A heuristic mapping is done between the xhr error code to the state of server connection.
const { code: originalCode, message } = err;
let code = null;
switch (originalCode) {
case 'ENOTFOUND':
// A socket operation failed because the network was down.
// falls through
case 'ENETDOWN':
// The range of the temporary ports for connection are all taken,
// This is temporal with many http requests, but should be counted as a network away event.
// falls through
case 'EADDRNOTAVAIL':
// The host server is unreachable, could be in a VPN.
// falls through
case 'EHOSTUNREACH':
// A request timeout is considered a network away event.
// falls through
case 'ETIMEDOUT':
case 'ESOCKETTIMEDOUT':
code = 'NETWORK_AWAY';
break;
case 'ECONNREFUSED':
// Server shut down or port no longer accessible.
if (_this2._heartbeatConnectedOnce) {
code = 'SERVER_CRASHED';
} else {
code = 'PORT_NOT_ACCESSIBLE';
}
break;
case 'ECONNRESET':
code = 'INVALID_CERTIFICATE';
break;
default:
code = originalCode;
break;}
_checkReconnectErrorType(originalCode) {
this._connectionResetCount++;
_this2._emitter.emit('heartbeat.error', { code, originalCode, message });
}})();
if (this._connectionResetCount >= ECONNRESET_ERRORS_IN_ROW_LIMIT) {
return 'INVALID_CERTIFICATE';
}
return originalCode;
}

@@ -156,9 +223,3 @@

onHeartbeatError(
callback)
{
onHeartbeatError(callback) {
return this._emitter.on('heartbeat.error', callback);

@@ -179,2 +240,6 @@ }

}
}}exports.XhrConnectionHeartbeat = XhrConnectionHeartbeat;
}
}
exports.XhrConnectionHeartbeat = XhrConnectionHeartbeat;

@@ -1,26 +0,26 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.execFile = execFile;
var _child_process = _interopRequireDefault(require("child_process"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
execFile = execFile;var _child_process = _interopRequireDefault(require('child_process'));function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}function execFile(
file,
args,
options)
{
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
function execFile(file, args, options) {
return new Promise((resolve, reject) => {
_child_process.default.execFile(
file,
args,
options,
(error, stdout, stderr) => {
_child_process.default.execFile(file, args, options, (error, stdout, stderr) => {
if (error != null) {

@@ -33,14 +33,3 @@ reject(error);

});
});
} /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
}

@@ -1,83 +0,70 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.onceEvent = onceEvent;
exports.onceEventOrError = onceEventOrError;
exports.onceEventArray = onceEventArray;
function _promise() {
const data = require("nuclide-commons/promise");
_promise = function () {
return data;
};
return data;
}
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
/**
* Creates a promise to await a single firing of `event` from `emitter`. This function only returns
* the first argument from the event.
*/
function onceEvent(emitter, event) {
return new Promise(resolve => {
emitter.once(event, resolve);
});
}
/**
* Creates a promise to await a single firing of `event` from `emitter`. This function only returns
* the first argument from the event.
*/
function onceEventOrError(emitter, event) {
const {
promise,
resolve,
reject
} = new (_promise().Deferred)();
emitter.once(event, resolve);
emitter.once('error', reject);
return (0, _promise().lastly)(promise, () => {
emitter.removeListener(event, resolve);
emitter.removeListener('error', reject);
});
}
/**
* Creates a promise to await a single firing of `event` from `emitter`. This function returns an
* array of all arguments from the event.
*/
onceEvent = onceEvent;exports.
onceEventOrError = onceEventOrError;exports.
onceEventArray = onceEventArray;var _promise;function _load_promise() {return _promise = require('nuclide-commons/promise');} /**
* Creates a promise to await a single firing of `event` from `emitter`. This function only returns
* the first argument from the event.
*/function onceEvent(emitter, event) {return new Promise(resolve => {emitter.once(event, resolve);});} /**
* Creates a promise to await a single firing of `event` from `emitter`. This function only returns
* the first argument from the event.
*/ /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/function onceEventOrError(emitter, event) {const { promise, resolve, reject } = new (_promise || _load_promise()).Deferred();emitter.once(event, resolve);emitter.once('error', reject);return (0, (_promise || _load_promise()).lastly)(promise, () => {emitter.removeListener(event, resolve);emitter.removeListener('error', reject);});} /**
* Creates a promise to await a single firing of `event` from `emitter`. This function returns an
* array of all arguments from the event.
*/function onceEventArray(emitter, event) {return new Promise(resolve => {emitter.once(event, (...args) => resolve(args));});}
function onceEventArray(emitter, event) {
return new Promise(resolve => {
emitter.once(event, (...args) => resolve(args));
});
}

@@ -1,254 +0,279 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });var _asyncToGenerator = _interopRequireDefault(require('async-to-generator'));
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _fs = _interopRequireDefault(require("fs"));
function _nuclideUri() {
const data = _interopRequireDefault(require("nuclide-commons/nuclideUri"));
_nuclideUri = function () {
return data;
};
return data;
}
function _rimraf() {
const data = _interopRequireDefault(require("rimraf"));
_rimraf = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
/**
* Searches upward through the filesystem from startDir to find a file with the
* given name.
* @param fileName The name of the file to find
* @param startDir Where to begin the search (e.g., cwd)
* @param stopDir (optional) Directory where we stop the search
* @return path to the nearest file, or null if none exists
*/
function findNearestFile(fileName, startDir, stopDir = null) {
const matcher = async path => {
const candidate = _nuclideUri().default.join(path, fileName);
const result = await isFile(candidate);
return result ? candidate : null;
};
return findNearest(matcher, startDir, stopDir);
}
/**
* Searches upward through the filesystem from pathToDirectory to find a
* directory with the given name.
* @param dirName The name of the directory to find
* @param startDir Where to begin the search (e.g., cwd)
* @param stopDir (optional) Directory where we stop the search
* @return path to the nearest directory, or null if none exists
*/
function findNearestDir(dirName, startDir, stopDir = null) {
const matcher = async path => {
const candidate = _nuclideUri().default.join(path, dirName);
const stats = await stat(candidate);
return stats && stats.isDirectory() ? candidate : null;
};
return findNearest(matcher, startDir, stopDir);
}
/**
* Simultaneously looks at directories between `startDir` and `stopDir` (or root dir),
* passing them to the provided `matcher` function and returning the string returned
* by the logically first (nearest) matcher, or `null` if no matchers matched.
* @param matcher: a function that returns the matched path if a match is found; otherwise null
* @param startDir: Where to begin the search
* @param stopDir: Where to stop the search (e.g., repository root), or null for filesystem root
* @return the nearest matched path to startDir if a match is found; otherwise null
*/
async function findNearest(matcher, startDir, stopDir) {
const candidates = [];
let candidateDir = startDir;
while (candidateDir !== stopDir) {
candidates.push(candidateDir);
const parentDir = _nuclideUri().default.dirname(candidateDir);
if (parentDir === candidateDir) {
// filesystem root reached
break;
} else {
candidateDir = parentDir;
}
}
const results = await Promise.all(candidates.map(matcher));
for (const result of results) {
if (result != null) {
return result;
}
}
return null;
}
/**
* @return whether path corresponds to an ordinary file.
*/
async function isFile(path) {
const stats = await stat(path);
return stats != null && stats.isFile();
}
/**
* async version of https://nodejs.org/api/fs.html#fs_fs_stat_path_callback.
* @return null if there is no such file or directory for path; otherwise, fs.Stats for path.
*/
/**
* Simultaneously looks at directories between `startDir` and `stopDir` (or root dir),
* passing them to the provided `matcher` function and returning the string returned
* by the logically first (nearest) matcher, or `null` if no matchers matched.
* @param matcher: a function that returns the matched path if a match is found; otherwise null
* @param startDir: Where to begin the search
* @param stopDir: Where to stop the search (e.g., repository root), or null for filesystem root
* @return the nearest matched path to startDir if a match is found; otherwise null
*/let findNearest = (() => {var _ref3 = (0, _asyncToGenerator.default)(
function* (
matcher,
startDir,
stopDir)
{
const candidates = [];
let candidateDir = startDir;
while (candidateDir !== stopDir) {
candidates.push(candidateDir);
const parentDir = (_nuclideUri || _load_nuclideUri()).default.dirname(candidateDir);
if (parentDir === candidateDir) {
// filesystem root reached
break;
} else {
candidateDir = parentDir;
}
async function stat(path) {
try {
const [stats] = await toPromise(_fs.default.stat)(path);
return stats;
} catch (error) {
if (error.code === 'ENOENT') {
return null;
}
const results = yield Promise.all(candidates.map(matcher));
for (const result of results) {
if (result != null) {
return result;
}
}
return null;
});return function findNearest(_x3, _x4, _x5) {return _ref3.apply(this, arguments);};})();
throw error;
}
}
/**
* @return whether path corresponds to an ordinary file.
*/let isFile = (() => {var _ref4 = (0, _asyncToGenerator.default)(
function* (path) {
const stats = yield stat(path);
return stats != null && stats.isFile();
});return function isFile(_x6) {return _ref4.apply(this, arguments);};})();
* async version of https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback.
*/
/**
* async version of https://nodejs.org/api/fs.html#fs_fs_stat_path_callback.
* @return null if there is no such file or directory for path; otherwise, fs.Stats for path.
*/let stat = (() => {var _ref5 = (0, _asyncToGenerator.default)(
function* (path) {
try {
const [stats] = yield toPromise(_fs.default.stat)(path);
return stats;
} catch (error) {
if (error.code === 'ENOENT') {
return null;
}
throw error;
}
});return function stat(_x7) {return _ref5.apply(this, arguments);};})();
function writeFile(file, data, options) {
return toPromise(_fs.default.writeFile)(file, data, options);
}
/**
* async version of https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback.
*/
* async version of https://nodejs.org/api/fs.html#fs_fs_readfile_file_options_callback.
*/
async function readFile(file, options = {}) {
const [data] = await toPromise(_fs.default.readFile)(file, options);
return data;
}
async function readFileAsString(file, encoding = 'utf8') {
const out = await readFile(file, {
encoding
});
if (!(typeof out === 'string')) {
throw new Error("Invariant violation: \"typeof out === 'string'\"");
}
return out;
}
async function readFileAsBuffer(file) {
const out = await readFile(file);
if (!(out instanceof Buffer)) {
throw new Error("Invariant violation: \"out instanceof Buffer\"");
}
return out;
}
/**
* async version of https://nodejs.org/api/fs.html#fs_fs_readfile_file_options_callback.
*/let readFile = (() => {var _ref6 = (0, _asyncToGenerator.default)(
function* (
file,
options = {})
{
const [data] = yield toPromise(_fs.default.readFile)(file, options);
return data;
});return function readFile(_x8) {return _ref6.apply(this, arguments);};})();let readFileAsString = (() => {var _ref7 = (0, _asyncToGenerator.default)(
* async version of https://nodejs.org/api/fs.html#fs_fs_mkdir_path_mode_callback.
* @param path directory to create.
* @param mode defaults to 0o777.
*/
function* (
file,
encoding = 'utf8')
{
const out = yield readFile(file, { encoding });if (!(
typeof out === 'string')) {throw new Error('Invariant violation: "typeof out === \'string\'"');}
return out;
});return function readFileAsString(_x9) {return _ref7.apply(this, arguments);};})();let readFileAsBuffer = (() => {var _ref8 = (0, _asyncToGenerator.default)(
function* (file) {
const out = yield readFile(file);if (!(
out instanceof Buffer)) {throw new Error('Invariant violation: "out instanceof Buffer"');}
return out;
});return function readFileAsBuffer(_x10) {return _ref8.apply(this, arguments);};})();
function mkdir(path, mode) {
return toPromise(_fs.default.mkdir)(path, mode);
}
/**
* async version of https://nodejs.org/api/fs.html#fs_fs_mkdir_path_mode_callback.
* @param path directory to create.
* @param mode defaults to 0o777.
*/
* @param prefix six random characters will be added to the end of this prefix.
* @param options can be a string specifying an encoding or an object with an `encoding` property.
*/
function mkdtemp(prefix, options) {
return toPromise(_fs.default.mkdtemp)(prefix, options).then(([tempDir]) => tempDir);
}
/**
* async version of https://nodejs.org/api/fs.html#fs_fs_mkdir_path_mode_callback.
* @param path directory to remove.
*/
function rmdir(path) {
return toPromise(_fs.default.rmdir)(path);
}
/**
* async version of https://nodejs.org/api/fs.html#fs_fs_mkdir_path_mode_callback.
* @param path file to remove.
*/
function unlink(path) {
return toPromise(_fs.default.unlink)(path);
}
/**
* async version of rimraf https://github.com/isaacs/rimraf#api
* removed the path recursively
* @param path directory or file to delete
* @param options
*/
async function remove(path, options = {}) {
await toPromise(_rimraf().default)(path, options);
}
/**
* Transforms any function that accepts callback as last parameter into
* a function that returns a Promise.
*
* We are assuming that the callback will have a form of (error, ...results) - the node style..
* The returned promise resolves with an array of results or rejects with an error.
*
* It's important to wrap the functions obtained this way with our own library
* like we do in `commons/fs.js` to proprly type it and surface a better API if possible.
*
* One could argue that repeating the Promise code each time would be a bit more performant
* but I think the code readability and brevity is more important for now. We can always
* optimize idividual functions when we see a bottleneck.
*/
function toPromise(func) {
return (...args) => {
return new Promise((resolve, reject) => {
args.push((err, ...results) => {
if (err != null) {
return reject(err);
}
resolve(results);
});
func(...args);
});
};
}
/**
* async version of rimraf https://github.com/isaacs/rimraf#api
* removed the path recursively
* @param path directory or file to delete
* @param options
*/let remove = (() => {var _ref9 = (0, _asyncToGenerator.default)(
function* (
path,
options = {})
{
yield toPromise((_rimraf || _load_rimraf()).default)(path, options);
});return function remove(_x11) {return _ref9.apply(this, arguments);};})();
/**
* Transforms any function that accepts callback as last parameter into
* a function that returns a Promise.
*
* We are assuming that the callback will have a form of (error, ...results) - the node style..
* The returned promise resolves with an array of results or rejects with an error.
*
* It's important to wrap the functions obtained this way with our own library
* like we do in `commons/fs.js` to proprly type it and surface a better API if possible.
*
* One could argue that repeating the Promise code each time would be a bit more performant
* but I think the code readability and brevity is more important for now. We can always
* optimize idividual functions when we see a bottleneck.
*/var _fs = _interopRequireDefault(require('fs'));var _nuclideUri;function _load_nuclideUri() {return _nuclideUri = _interopRequireDefault(require('nuclide-commons/nuclideUri'));}var _rimraf;function _load_rimraf() {return _rimraf = _interopRequireDefault(require('rimraf'));}function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };} /**
* Searches upward through the filesystem from startDir to find a file with the
* given name.
* @param fileName The name of the file to find
* @param startDir Where to begin the search (e.g., cwd)
* @param stopDir (optional) Directory where we stop the search
* @return path to the nearest file, or null if none exists
*/function findNearestFile(fileName, startDir, stopDir = null) {const matcher = (() => {var _ref = (0, _asyncToGenerator.default)(function* (path) {const candidate = (_nuclideUri || _load_nuclideUri()).default.join(path, fileName);const result = yield isFile(candidate);return result ? candidate : null;});return function matcher(_x) {return _ref.apply(this, arguments);};})();return findNearest(matcher, startDir, stopDir);} /**
* Searches upward through the filesystem from pathToDirectory to find a
* directory with the given name.
* @param dirName The name of the directory to find
* @param startDir Where to begin the search (e.g., cwd)
* @param stopDir (optional) Directory where we stop the search
* @return path to the nearest directory, or null if none exists
*/ /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/function findNearestDir(dirName, startDir, stopDir = null) {const matcher = (() => {var _ref2 = (0, _asyncToGenerator.default)(function* (path) {const candidate = (_nuclideUri || _load_nuclideUri()).default.join(path, dirName);const stats = yield stat(candidate);return stats && stats.isDirectory() ? candidate : null;});return function matcher(_x2) {return _ref2.apply(this, arguments);};})();return findNearest(matcher, startDir, stopDir);}function writeFile(file, data, options) {return toPromise(_fs.default.writeFile)(file, data, options);}function mkdir(path, mode) {return toPromise(_fs.default.mkdir)(path, mode);} /**
* @param prefix six random characters will be added to the end of this prefix.
* @param options can be a string specifying an encoding or an object with an `encoding` property.
*/function mkdtemp(prefix, options) {return toPromise(_fs.default.mkdtemp)(prefix, options).then(([tempDir]) => tempDir);} /**
* async version of https://nodejs.org/api/fs.html#fs_fs_mkdir_path_mode_callback.
* @param path directory to remove.
*/function rmdir(path) {return toPromise(_fs.default.rmdir)(path);} /**
* async version of https://nodejs.org/api/fs.html#fs_fs_mkdir_path_mode_callback.
* @param path file to remove.
*/function unlink(path) {return toPromise(_fs.default.unlink)(path);}function toPromise(func) {return (...args) => {return new Promise((resolve, reject) => {args.push((err, ...results) => {if (err != null) {return reject(err);}resolve(results);});func(...args);});};}exports.default = { findNearestDir, findNearestFile, isFile, mkdir, mkdtemp, readFileAsBuffer, readFileAsString, remove, rmdir, stat, unlink, writeFile };
var _default = {
findNearestDir,
findNearestFile,
isFile,
mkdir,
mkdtemp,
readFileAsBuffer,
readFileAsString,
readFile,
remove,
rmdir,
stat,
unlink,
writeFile
};
exports.default = _default;

@@ -1,31 +0,42 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getVersion = getVersion;
function _package() {
const data = require("nuclide-commons/package");
_package = function () {
return data;
};
return data;
}
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* strict
* @format
*/
// eslint-disable-next-line
let version;
function getVersion() {
if (!version) {
// Don't use require() because it may be reading from the module cache.
// Do use require.resolve so the paths can be codemoded in the future.
const packageJsonPath = require.resolve("../../package.json");
version = (0, _package().getPackageMinorVersion)(packageJsonPath);
}
getVersion = getVersion;var _package;function _load_package() {return _package = require('nuclide-commons/package');}let version; /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/ // eslint-disable-next-line
function getVersion() {if (!version) {// Don't use require() because it may be reading from the module cache.
// Do use require.resolve so the paths can be codemoded in the future.
const packageJsonPath = require.resolve('../../package.json');version = (0, (_package || _load_package()).getPackageMinorVersion)(packageJsonPath);}return version;}
return version;
}

@@ -1,38 +0,39 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.parsePorts = parsePorts;
exports.scanPortsToListen = scanPortsToListen;
var _https = _interopRequireDefault(require("https"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* strict
* @format
*/
function parsePorts(portsDescriptor) {
const ranges = [];
const descriptors = portsDescriptor.split(',').map(x => x.trim()).filter(x => x !== '');
parsePorts = parsePorts;function parsePorts(portsDescriptor) {
const ranges = [];
const descriptors = portsDescriptor.
split(',').
map(x => x.trim()).
filter(x => x !== '');
for (const descriptor of descriptors) {
let range = null;
if (/^\d+$/.test(descriptor)) {
range = {
start: parseNonNegativeIntOrThrow(descriptor),
length: 1 };
length: 1
};
} else {
const match = descriptor.match(/^(\d+)-(\d+)$/);
if (match != null) {

@@ -44,8 +45,9 @@ const start = parseNonNegativeIntOrThrow(match[1]);

start,
length: delta + (delta < 0 ? -1 : 1) };
length: delta + (delta < 0 ? -1 : 1)
};
} else {
throw Error(`Could not parse ports from: "${descriptor}".`);
throw new Error(`Could not parse ports from: "${descriptor}".`);
}
}
ranges.push(range);

@@ -56,38 +58,88 @@ }

}
/**
* Class that is an iterable for port numbers.
*/
* Class that is an iterable for port numbers.
*/
// $FlowIssue https://github.com/facebook/flow/issues/2286
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/ /**
* Represents a range of ports by an initial integer paired with the number of
* elements in the range. If `length` is negative, then the range counts "down"
* from `start` instead of counting "up". `length` should never be zero.
*/class Ports {constructor(ranges) {this._ranges = ranges;} // $FlowIssue https://github.com/facebook/flow/issues/2286
*[Symbol.iterator]() {for (const _ref of this._ranges) {const { start, length } = _ref;const delta = length < 0 ? -1 : 1;let offset = 0;while (offset !== length) {yield start + offset;offset += delta;}
class Ports {
constructor(ranges) {
this._ranges = ranges;
} // $FlowIssue https://github.com/facebook/flow/issues/2286
*[Symbol.iterator]() {
for (const _ref of this._ranges) {
const {
start,
length
} = _ref;
const delta = length < 0 ? -1 : 1;
let offset = 0;
while (offset !== length) {
yield start + offset;
offset += delta;
}
}
}}
}
}
function parseNonNegativeIntOrThrow(str) {
const value = parseInt(str, 10);
if (isNaN(value)) {
throw Error(`"${str}" could not be parsed as a valid integer.`);
throw new Error(`"${str}" could not be parsed as a valid integer.`);
} else if (value === Infinity || value === -Infinity) {
throw Error(`${str} parses to an extrema: ${value}.`);
throw new Error(`${str} parses to an extrema: ${value}.`);
} else if (value < 0) {
throw Error(`${str} parses to a negative number: ${value}.`);
throw new Error(`${str} parses to a negative number: ${value}.`);
} else {
return value;
}
}
/**
* Attempts to have the server listen to the specified port.
* Returns true if successful or false if the port is already in use.
* Any other errors result in a rejection.
*/
function tryListen(server, port) {
return new Promise((resolve, reject) => {
function onError(error) {
if (error.errno === 'EADDRINUSE') {
return resolve(false);
}
reject(error);
}
server.once('error', onError);
server.listen(port, () => {
// Let errors after the initial listen fall through to the global exception handler.
server.removeListener('error', onError);
resolve(true);
});
});
}
/**
* Scan ports to listen to an available port
* @param ports string e.g. '8082, 8089, 10222-10234'
*/
async function scanPortsToListen(server, ports) {
let found = false;
for (const port of parsePorts(ports)) {
// eslint-disable-next-line no-await-in-loop
if (await tryListen(server, port)) {
found = true;
break;
}
}
return found;
}

@@ -1,30 +0,41 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.question = question;
var _readline = _interopRequireDefault(require("readline"));
var _stream = _interopRequireDefault(require("stream"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* strict
* @format
*/
function question(query, hideInput) {
// http://stackoverflow.com/questions/24037545/how-to-hide-password-in-the-nodejs-console
let output;
let muted = false;
if (hideInput) {
output = new _stream.default.Writable({
write(chunk, encoding, callback) {
if (!muted) {
process.stdout.write(chunk, encoding);
}
callback();
}
question = question;var _readline = _interopRequireDefault(require('readline'));var _stream = _interopRequireDefault(require('stream'));function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };} /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/function question(query, hideInput) {// http://stackoverflow.com/questions/24037545/how-to-hide-password-in-the-nodejs-console
let output;let muted = false;if (hideInput) {output = new _stream.default.Writable({ write(chunk, encoding, callback) {if (!muted) {process.stdout.write(chunk, encoding);}callback();
} });
});
} else {

@@ -37,3 +48,4 @@ output = process.stdout;

output,
terminal: true });
terminal: true
});

@@ -40,0 +52,0 @@ return new Promise(resolve => {

@@ -1,32 +0,32 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getUsername = getUsername;
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* strict
* @format
*/
function getUsername() {
// It is slightly more robust to get the uid and look it up in /etc/whateveritis.
const {
env
} = process; // flowlint-next-line sketchy-null-string:off
const username = env.LOGNAME || env.USER || env.LNAME || env.USERNAME;
if (!(username != null)) {
throw new Error("Invariant violation: \"username != null\"");
}
getUsername = getUsername;function getUsername() {
// It is slightly more robust to get the uid and look it up in /etc/whateveritis.
const { env } = process;
// flowlint-next-line sketchy-null-string:off
const username = env.LOGNAME || env.USER || env.LNAME || env.USERNAME;if (!(
username != null)) {throw new Error('Invariant violation: "username != null"');}
return username;
} /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
}

@@ -1,64 +0,143 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.HEARTBEAT_CHANNEL = undefined;var _log4js;
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.BigDigServer = exports.CLOSE_TAG = exports.HEARTBEAT_CHANNEL = void 0;
function _ws() {
const data = _interopRequireDefault(require("ws"));
_ws = function () {
return data;
};
return data;
}
var _https = _interopRequireDefault(require("https"));
function _log4js() {
const data = require("log4js");
_log4js = function () {
return data;
};
return data;
}
var _url = _interopRequireDefault(require("url"));
var _RxMin = require("rxjs/bundles/Rx.min.js");
function _getVersion() {
const data = require("../common/getVersion");
_getVersion = function () {
return data;
};
return data;
}
function _WebSocketTransport() {
const data = require("../socket/WebSocketTransport");
function _load_log4js() {return _log4js = require('log4js');}
_WebSocketTransport = function () {
return data;
};
var _url = _interopRequireDefault(require('url'));
var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js');var _getVersion;
function _load_getVersion() {return _getVersion = require('../common/getVersion');}var _WebSocketTransport;
return data;
}
function _load_WebSocketTransport() {return _WebSocketTransport = require('../socket/WebSocketTransport');}var _QueuedAckTransport;
function _load_QueuedAckTransport() {return _QueuedAckTransport = require('../socket/QueuedAckTransport');}function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}
function _QueuedAckTransport() {
const data = require("../socket/QueuedAckTransport");
const HEARTBEAT_CHANNEL = exports.HEARTBEAT_CHANNEL = 'big-dig-heartbeat'; /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
class BigDigServer {
_QueuedAckTransport = function () {
return data;
};
return data;
}
function _ports() {
const data = require("../common/ports");
_ports = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* strict-local
* @format
*/
const HEARTBEAT_CHANNEL = 'big-dig-heartbeat';
exports.HEARTBEAT_CHANNEL = HEARTBEAT_CHANNEL;
const CLOSE_TAG = 'big-dig-close-connection';
exports.CLOSE_TAG = CLOSE_TAG;
class BigDigServer {
/**
* Note: The webSocketServer must be running on top of the httpsServer.
* Note: This BigDigServer is responsible for closing httpServer and wss.
*/
* Note: The webSocketServer must be running on top of the httpsServer.
* Note: This BigDigServer is responsible for closing httpServer and wss.
*/
constructor(httpsServer, webSocketServer) {
this._logger = (0, (_log4js || _load_log4js()).getLogger)();
this._logger = (0, _log4js().getLogger)();
this._tagToSubscriber = new Map();
this._httpsServer = httpsServer;
this._httpsServer.on('request', this._onHttpsRequest.bind(this));
this._clientIdToTransport = new Map();
this._webSocketServer = webSocketServer;
this._webSocketServer.on(
'connection',
this._onWebSocketConnection.bind(this));
this._webSocketServer.on('connection', this._onWebSocketConnection.bind(this));
}
static async createServer(options) {
const webServer = _https.default.createServer(options.webServer);
if (!(await (0, _ports().scanPortsToListen)(webServer, options.ports))) {
throw new Error(`All ports in range "${options.ports}" are already in use`);
}
const webSocketServer = new (_ws().default.Server)({
server: webServer,
perMessageDeflate: true
}); // Let unhandled WS server errors go through to the global exception handler.
// $FlowIgnore
const launcher = require(options.absolutePathToServerMain);
const tunnelLauncher = require("../services/tunnel/launcher");
const thriftLauncher = require("../services/thrift/launcher");
const bigDigServer = new BigDigServer(webServer, webSocketServer);
await launcher(bigDigServer);
await tunnelLauncher(bigDigServer);
await thriftLauncher(bigDigServer);
return bigDigServer;
}
addSubscriber(tag, subscriber) {
if (tag === CLOSE_TAG) {
throw new Error(`Tag ${CLOSE_TAG} is reserved; cannot subscribe.`);
}
const existingSubscriber = this._tagToSubscriber.get(tag);
if (existingSubscriber == null) {

@@ -71,25 +150,33 @@ // TODO(mbolin): WS connections that were created before this subscriber

} else {
throw Error(`subscriber is already registered for ${tag}`);
throw new Error(`subscriber is already registered for ${tag}`);
}
}
_onHttpsRequest(
request,
response)
{
const { pathname } = _url.default.parse(request.url);
getPort() {
return this._httpsServer.address().port;
}
_onHttpsRequest(request, response) {
const {
pathname
} = _url.default.parse(request.url);
if (request.method === 'POST' && pathname === `/v1/${HEARTBEAT_CHANNEL}`) {
response.write((0, (_getVersion || _load_getVersion()).getVersion)());
response.write((0, _getVersion().getVersion)());
response.end();
return;
}
this._logger.info(
`Ignored HTTPS ${request.method} request for ${request.url}`);
this._logger.info(`Ignored HTTPS ${request.method} request for ${request.url}`);
}
_onWebSocketConnection(ws, req) {
const { pathname } = _url.default.parse(req.url);
const {
pathname
} = _url.default.parse(req.url);
const clientId = req.headers.client_id;
this._logger.info(`connection negotiation via path ${String(pathname)}`);
this._logger.info(`received client_id in header ${clientId}`);

@@ -99,2 +186,3 @@

this._logger.info(`Ignored WSS connection for ${String(pathname)}`);
return;

@@ -104,30 +192,36 @@ }

const cachedTransport = this._clientIdToTransport.get(clientId);
const wsTransport = new (_WebSocketTransport || _load_WebSocketTransport()).WebSocketTransport(clientId, ws);
const wsTransport = new (_WebSocketTransport().WebSocketTransport)(clientId, ws);
if (cachedTransport == null) {
this._logger.info(`on connection the clientId is ${clientId}`);
const qaTransport = new (_QueuedAckTransport || _load_QueuedAckTransport()).QueuedAckTransport(clientId, wsTransport);
this._clientIdToTransport.set(clientId, qaTransport);
const qaTransport = new (_QueuedAckTransport().QueuedAckTransport)(clientId, wsTransport);
// Every subscriber must be notified of the new connection because it may
this._clientIdToTransport.set(clientId, qaTransport); // Every subscriber must be notified of the new connection because it may
// want to broadcast messages to it.
const tagToTransport = new Map();
for (const [tag, subscriber] of this._tagToSubscriber) {
const transport = new InternalTransport(tag, qaTransport);
this._logger.info(`Created new InternalTransport for ${tag}`);
tagToTransport.set(tag, transport);
subscriber.onConnection(transport);
}
} // subsequent messages will be BigDig messages
// TODO: could the message be a Buffer?
// subsequent messages will be BigDig messages
// TODO: could the message be a Buffer?
qaTransport.onMessage().subscribe(message => {
this._handleBigDigMessage(tagToTransport, message);
});
this._handleBigDigMessage(tagToTransport, qaTransport, message);
}); // TODO: Either garbage collect inactive transports, or implement
// an explicit "close" action in the big-dig protocol.
} else {
if (!(clientId === cachedTransport.id)) {
throw new Error("Invariant violation: \"clientId === cachedTransport.id\"");
}
// TODO: Either garbage collect inactive transports, or implement
// an explicit "close" action in the big-dig protocol.
} else {if (!(
clientId === cachedTransport.id)) {throw new Error('Invariant violation: "clientId === cachedTransport.id"');}
cachedTransport.reconnect(wsTransport);

@@ -137,33 +231,43 @@ }

_handleBigDigMessage(
tagToTransport,
message)
{
_handleBigDigMessage(tagToTransport, qaTransport, message) {
// The message must start with a header identifying its route.
const index = message.indexOf('\0');
const tag = message.substring(0, index);
const body = message.substring(index + 1);
const transport = tagToTransport.get(tag);
if (transport != null) {
transport.broadcastMessage(body);
if (tag === CLOSE_TAG) {
for (const transport of tagToTransport.values()) {
transport.close();
}
tagToTransport.clear();
this._clientIdToTransport.delete(qaTransport.id);
qaTransport.close();
} else {
this._logger.info(`No route for ${tag}.`);
const body = message.substring(index + 1);
const transport = tagToTransport.get(tag);
if (transport != null) {
transport.broadcastMessage(body);
} else {
this._logger.info(`No route for ${tag}.`);
}
}
}}exports.default = BigDigServer;
}
}
/**
* Note that an InternalTransport maintains a reference to a WS connection.
* It is imperative that it does not leak this reference such that a client
* holds onto it and prevents it from being garbage-collected after the
* connection is terminated.
*/
class InternalTransport {
* Note that an InternalTransport maintains a reference to a WS connection.
* It is imperative that it does not leak this reference such that a client
* holds onto it and prevents it from being garbage-collected after the
* connection is terminated.
*/
exports.BigDigServer = BigDigServer;
class InternalTransport {
constructor(tag, ws) {
this._messages = new _rxjsBundlesRxMinJs.Subject();
this._messages = new _RxMin.Subject();
this._tag = tag;

@@ -189,2 +293,4 @@ this._transport = ws;

this._messages.complete();
}}
}
}

@@ -1,189 +0,121 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.generateCertificates = undefined;var _asyncToGenerator = _interopRequireDefault(require('async-to-generator'));
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.generateCertificates = generateCertificates;
var _net = _interopRequireDefault(require("net"));
var _os = _interopRequireDefault(require("os"));
function _nuclideUri() {
const data = _interopRequireDefault(require("nuclide-commons/nuclideUri"));
_nuclideUri = function () {
return data;
};
return data;
}
function _child_process() {
const data = require("../common/child_process");
_child_process = function () {
return data;
};
return data;
}
function _fs() {
const data = _interopRequireDefault(require("../common/fs"));
_fs = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
/**
* If successful, this will return a set of paths where all of the certificate info was written.
*/
async function generateCertificates(clientCommonName, serverCommonName, openSSLConfigPath, expirationDays) {
// Set the process umask to 0077 to ensure that certificates have 0700 permissions.
// The spawned OpenSSL processes will inherit the umask.
const oldUmask = process.umask();
process.umask(0o77);
try {
const paths = await generateKeyPairPaths();
const env = generateEnvironmentForOpenSSLCalls(serverCommonName);
await generateCA(paths.caKey, paths.caCert, expirationDays, env);
await Promise.all([generateKeyAndCertificate(paths.caKey, paths.caCert, expirationDays, paths.serverKey, paths.serverCsr, paths.serverCert, openSSLConfigPath, serverCommonName, 1, env), generateKeyAndCertificate(paths.caKey, paths.caCert, expirationDays, paths.clientKey, paths.clientCsr, paths.clientCert, openSSLConfigPath, clientCommonName, 2, env)]);
return paths;
} finally {
process.umask(oldUmask);
}
}
async function generateCA(caKeyPath, caCertPath, expirationDays, env) {
const command = 'openssl';
const options = {
env
};
await (0, _child_process().execFile)(command, ['genrsa', '-out', caKeyPath, '1024'], options);
await (0, _child_process().execFile)(command, ['req', '-new', '-x509', '-days', String(expirationDays), '-key', caKeyPath, '-out', caCertPath, '-batch'], options);
}
async function generateKeyAndCertificate(caKeyPath, caCertPath, expirationDays, keyFilePath, csrFilePath, certFilePath, openSSLConfigPath, commonName, serial, env) {
const command = 'openssl';
const options = {
env
};
await (0, _child_process().execFile)(command, ['genrsa', '-out', keyFilePath, '1024'], options);
await (0, _child_process().execFile)(command, ['req', '-new', '-key', keyFilePath, '-out', csrFilePath, '-subj', `/CN=${commonName}`, '-config', openSSLConfigPath], options);
await (0, _child_process().execFile)(command, ['x509', '-req', '-days', String(expirationDays), '-in', csrFilePath, '-CA', caCertPath, '-CAkey', caKeyPath, '-set_serial', String(serial), '-out', certFilePath, '-extensions', 'v3_req', '-extfile', openSSLConfigPath], options);
}
/**
* If successful, this will return a set of paths where all of the certificate info was written.
*/let generateCertificates = exports.generateCertificates = (() => {var _ref = (0, _asyncToGenerator.default)(
function* (
clientCommonName,
serverCommonName,
openSSLConfigPath,
expirationDays)
{
// Set the process umask to 0077 to ensure that certificates have 0700 permissions.
// The spawned OpenSSL processes will inherit the umask.
const oldUmask = process.umask();
process.umask(0o77);
try {
const paths = yield generateKeyPairPaths();
const env = generateEnvironmentForOpenSSLCalls(serverCommonName);
yield generateCA(paths.caKey, paths.caCert, expirationDays, env);
yield Promise.all([
generateKeyAndCertificate(
paths.caKey,
paths.caCert,
expirationDays,
paths.serverKey,
paths.serverCsr,
paths.serverCert,
openSSLConfigPath,
serverCommonName,
1,
env),
* Creates a new temporary directory where all of the certificate data for one instance
* of the server should be written.
*/
generateKeyAndCertificate(
paths.caKey,
paths.caCert,
expirationDays,
paths.clientKey,
paths.clientCsr,
paths.clientCert,
openSSLConfigPath,
clientCommonName,
2,
env)]);
async function generateKeyPairPaths() {
const certsDir = await _fs().default.mkdtemp(_nuclideUri().default.join(_os.default.tmpdir(), '.big-dig-certs'));
return paths;
} finally {
process.umask(oldUmask);
}
});return function generateCertificates(_x, _x2, _x3, _x4) {return _ref.apply(this, arguments);};})(); /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/let generateCA = (() => {var _ref2 = (0, _asyncToGenerator.default)(function* (caKeyPath, caCertPath, expirationDays, env) {const command = 'openssl';const options = { env };yield (0, (_child_process || _load_child_process()).execFile)(command, ['genrsa', '-out', caKeyPath, '1024'], options);
yield (0, (_child_process || _load_child_process()).execFile)(
command,
[
'req',
'-new',
'-x509',
'-days',
String(expirationDays),
'-key',
caKeyPath,
'-out',
caCertPath,
'-batch'],
const pathPrefix = _nuclideUri().default.join(certsDir, 'big-dig');
options);
return {
certsDir,
caKey: `${pathPrefix}.ca.key`,
caCert: `${pathPrefix}.ca.crt`,
serverKey: `${pathPrefix}.server.key`,
serverCsr: `${pathPrefix}.server.csr`,
serverCert: `${pathPrefix}.server.cert`,
clientKey: `${pathPrefix}.client.key`,
clientCsr: `${pathPrefix}.client.csr`,
clientCert: `${pathPrefix}.client.cert`
};
}
});return function generateCA(_x5, _x6, _x7, _x8) {return _ref2.apply(this, arguments);};})();let generateKeyAndCertificate = (() => {var _ref3 = (0, _asyncToGenerator.default)(
function* (
caKeyPath,
caCertPath,
expirationDays,
keyFilePath,
csrFilePath,
certFilePath,
openSSLConfigPath,
commonName,
serial,
env)
{
const command = 'openssl';
const options = { env };
yield (0, (_child_process || _load_child_process()).execFile)(command, ['genrsa', '-out', keyFilePath, '1024'], options);
yield (0, (_child_process || _load_child_process()).execFile)(
command,
[
'req',
'-new',
'-key',
keyFilePath,
'-out',
csrFilePath,
'-subj',
`/CN=${commonName}`,
'-config',
openSSLConfigPath],
options);
yield (0, (_child_process || _load_child_process()).execFile)(
command,
[
'x509',
'-req',
'-days',
String(expirationDays),
'-in',
csrFilePath,
'-CA',
caCertPath,
'-CAkey',
caKeyPath,
'-set_serial',
String(serial),
'-out',
certFilePath,
'-extensions',
'v3_req',
'-extfile',
openSSLConfigPath],
options);
});return function generateKeyAndCertificate(_x9, _x10, _x11, _x12, _x13, _x14, _x15, _x16, _x17, _x18) {return _ref3.apply(this, arguments);};})();
/**
* Creates a new temporary directory where all of the certificate data for one instance
* of the server should be written.
*/let generateKeyPairPaths = (() => {var _ref4 = (0, _asyncToGenerator.default)(
function* () {
const certsDir = yield (_fs || _load_fs()).default.mkdtemp(
(_nuclideUri || _load_nuclideUri()).default.join(_os.default.tmpdir(), '.big-dig-certs'));
const pathPrefix = (_nuclideUri || _load_nuclideUri()).default.join(certsDir, 'big-dig');
return {
certsDir,
caKey: `${pathPrefix}.ca.key`,
caCert: `${pathPrefix}.ca.crt`,
serverKey: `${pathPrefix}.server.key`,
serverCsr: `${pathPrefix}.server.csr`,
serverCert: `${pathPrefix}.server.cert`,
clientKey: `${pathPrefix}.client.key`,
clientCsr: `${pathPrefix}.client.csr`,
clientCert: `${pathPrefix}.client.cert` };
});return function generateKeyPairPaths() {return _ref4.apply(this, arguments);};})();var _net = _interopRequireDefault(require('net'));var _os = _interopRequireDefault(require('os'));var _nuclideUri;function _load_nuclideUri() {return _nuclideUri = _interopRequireDefault(require('nuclide-commons/nuclideUri'));}var _child_process;function _load_child_process() {return _child_process = require('../common/child_process');}var _fs;function _load_fs() {return _fs = _interopRequireDefault(require('../common/fs'));}function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}
function generateEnvironmentForOpenSSLCalls(serverCommonName) {
const env = Object.assign({}, process.env);
if (process.platform === 'darwin') {

@@ -193,9 +125,8 @@ // High Sierra comes with LibreSSL by default, which is not supported.

env.PATH = '/usr/local/opt/openssl/bin:' + env.PATH;
}
// Usually, we don't have to make the common name a SAN,
} // Usually, we don't have to make the common name a SAN,
// but our openssl.cnf requires a value via $OPENSSL_SAN.
env.OPENSSL_SAN = _net.default.isIP(serverCommonName) ?
`IP:${serverCommonName}` :
`DNS.1:${serverCommonName}`;
env.OPENSSL_SAN = _net.default.isIP(serverCommonName) ? `IP:${serverCommonName}` : `DNS.1:${serverCommonName}`;
return env;
}

@@ -16,3 +16,3 @@ /**

prefer-object-spread/prefer-object-spread: 0,
rulesdir/no-commonjs: 0,
nuclide-internal/no-commonjs: 0,
*/

@@ -19,0 +19,0 @@

@@ -1,130 +0,191 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.parseArgsAndRunMain = undefined;var _asyncToGenerator = _interopRequireDefault(require('async-to-generator'));
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.parseArgsAndRunMain = parseArgsAndRunMain;
function _log4js() {
const data = _interopRequireDefault(require("log4js"));
_log4js = function () {
return data;
};
return data;
}
function _nuclideUri() {
const data = _interopRequireDefault(require("nuclide-commons/nuclideUri"));
_nuclideUri = function () {
return data;
};
return data;
}
var _os = _interopRequireDefault(require("os"));
function _username() {
const data = require("../common/username");
_username = function () {
return data;
};
return data;
}
function _main() {
const data = require("./main");
_main = function () {
return data;
};
return data;
}
function _ports() {
const data = require("../common/ports");
_ports = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* strict-local
* @format
*/
_log4js().default.configure({
appenders: [{
type: 'file',
filename: _nuclideUri().default.join(_os.default.tmpdir(), 'big-dig-cli.log')
}, {
type: 'console'
}]
});
const DEFAULT_PORTS = '0';
const DEFAULT_TIMEOUT = 60000;
/**
* @param absolutePathToServerMain The code that bootstraps the server will load the code at this
* path via require(). It is expected to have a default export that is a function that takes the
* WebSocket server created by Big Dig, along with other arguments, and starts the main
* server [that is using Big Dig as a building block].
*/
async function parseArgsAndRunMain(absolutePathToServerMain) {
// All arguments expect for the last one are ignored.
const params = JSON.parse(process.argv[process.argv.length - 1]);
const {
cname,
expiration,
exclusive,
jsonOutputFile,
caPath,
serverCertPath,
serverKeyPath
} = params;
let {
ports,
timeout
} = params;
if (cname != null && (typeof cname !== 'string' || cname.length === 0)) {
throw new Error(`cname must be a non-empty string but was: '${cname}'`);
}
if (typeof jsonOutputFile !== 'string') {
throw new Error('Must specify jsonOutputFile');
} // port arg validation
if (ports == null) {
ports = DEFAULT_PORTS;
}
if (typeof ports !== 'string') {
throw new Error(`ports must be specified as string but was: '${ports}'`);
} // This will throw an exception if the ports string is invalid.
(0, _ports().parsePorts)(ports);
if (timeout == null) {
timeout = DEFAULT_TIMEOUT;
}
if (typeof timeout !== 'number') {
throw new Error(`timeout must be specified as number but was: '${timeout}'`);
} // expiration arg validation
if (typeof expiration !== 'string') {
throw new Error(`expiration must be specified as string but was: '${expiration}'`);
}
const expirationMatch = expiration.match(/^(\d+)d$/);
if (expirationMatch == null) {
throw new Error(`expiration must be /(\\d+)d/ but was: '${expiration}'`);
}
const expirationDays = parseInt(expirationMatch[1], 10);
if (expirationDays <= 0) {
throw new Error(`expiration must be >0 but was ${expirationDays}`);
}
if (exclusive != null && (typeof exclusive !== 'string' || !exclusive.match(/^[\w\d][\w\d-]*$/))) {
throw new Error(`exclusive must be a valid identifier: '${exclusive}'`);
}
let certificateStrategy;
/**
* @param absolutePathToServerMain The code that bootstraps the server will load the code at this
* path via require(). It is expected to have a default export that is a function that takes the
* WebSocket server created by Big Dig, along with other arguments, and starts the main
* server [that is using Big Dig as a building block].
*/let parseArgsAndRunMain = exports.parseArgsAndRunMain = (() => {var _ref = (0, _asyncToGenerator.default)(
function* (absolutePathToServerMain) {
// All arguments expect for the last one are ignored.
const params = JSON.parse(
process.argv[process.argv.length - 1]);
const { cname, expiration, exclusive, jsonOutputFile } = params;
let { ports, timeout } = params;
if (typeof cname !== 'string') {
throw Error(`cname must be specified as string but was: '${cname}'`);
if (caPath != null || serverCertPath != null || serverKeyPath != null) {
if (typeof caPath !== 'string' || typeof serverCertPath !== 'string' || typeof serverKeyPath !== 'string') {
throw new Error('need either all or none of caPath, serverCertPath and serverKeyPath');
}
if (typeof jsonOutputFile !== 'string') {
throw Error('Must specify jsonOutputFile');
}
// port arg validation
if (ports == null) {
ports = DEFAULT_PORTS;
}
if (typeof ports !== 'string') {
throw Error(`ports must be specified as string but was: '${ports}'`);
}
// This will throw an exception if the ports string is invalid.
(0, (_ports || _load_ports()).parsePorts)(ports);
certificateStrategy = {
type: 'reuse',
paths: {
caCert: caPath,
serverCert: serverCertPath,
serverKey: serverKeyPath
}
};
} else {
certificateStrategy = {
type: 'generate',
clientCommonName: 'nuclide',
serverCommonName: cname != null ? cname : `${(0, _username().getUsername)()}.nuclide.${_os.default.hostname()}`,
openSSLConfigPath: require.resolve("./openssl.cnf")
};
}
if (timeout == null) {
timeout = DEFAULT_TIMEOUT;
}
if (typeof timeout !== 'number') {
throw Error(`timeout must be specified as number but was: '${timeout}'`);
}
// expiration arg validation
if (typeof expiration !== 'string') {
throw Error(
`expiration must be specified as string but was: '${expiration}'`);
}
const expirationMatch = expiration.match(/^(\d+)d$/);
if (expirationMatch == null) {
throw Error(`expiration must be /(\\d+)d/ but was: '${expiration}'`);
}
const expirationDays = parseInt(expirationMatch[1], 10);
if (expirationDays <= 0) {
throw Error(`expiration must be >0 but was ${expirationDays}`);
}
if (
exclusive != null && (
typeof exclusive !== 'string' || !exclusive.match(/^[\w\d][\w\d-]*$/)))
{
throw Error(`exclusive must be a valid identifier: '${exclusive}'`);
}
const clientCommonName = 'nuclide';
const serverCommonName = cname || `${(0, (_username || _load_username()).getUsername)()}.nuclide.${_os.default.hostname()}`;
const openSSLConfigPath = require.resolve('./openssl.cnf');
yield (0, (_main || _load_main()).generateCertificatesAndStartServer)({
clientCommonName,
serverCommonName,
openSSLConfigPath,
ports,
timeout,
expirationDays,
exclusive,
jsonOutputFile,
absolutePathToServerMain,
serverParams: params.serverParams });
});return function parseArgsAndRunMain(_x) {return _ref.apply(this, arguments);};})();var _log4js;function _load_log4js() {return _log4js = _interopRequireDefault(require('log4js'));}var _nuclideUri;function _load_nuclideUri() {return _nuclideUri = _interopRequireDefault(require('nuclide-commons/nuclideUri'));}var _os = _interopRequireDefault(require('os'));var _username;function _load_username() {return _username = require('../common/username');}var _main;function _load_main() {return _main = require('./main');}var _ports;function _load_ports() {return _ports = require('../common/ports');}function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };} /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/(_log4js || _load_log4js()).default.configure({ appenders: [{ type: 'file', filename: (_nuclideUri || _load_nuclideUri()).default.join(_os.default.tmpdir(), 'big-dig-cli.log') }, { type: 'console' }] });const DEFAULT_PORTS = '0';const DEFAULT_TIMEOUT = 60000;
await (0, _main().startServer)({
certificateStrategy,
ports,
timeout,
expirationDays,
exclusive,
jsonOutputFile,
absolutePathToServerMain,
serverParams: params.serverParams
});
}

@@ -16,3 +16,3 @@ /**

prefer-object-spread/prefer-object-spread: 0,
rulesdir/no-commonjs: 0,
nuclide-internal/no-commonjs: 0,
*/

@@ -19,0 +19,0 @@

@@ -1,175 +0,214 @@

'use strict';var _asyncToGenerator = _interopRequireDefault(require('async-to-generator'));let handleLaunchParams = (() => {var _ref = (0, _asyncToGenerator.default)(
"use strict";
var _fs = _interopRequireDefault(require("fs"));
function _log4js() {
const data = _interopRequireDefault(require("log4js"));
_log4js = function () {
return data;
};
return data;
}
function _nuclideUri() {
const data = _interopRequireDefault(require("nuclide-commons/nuclideUri"));
_nuclideUri = function () {
return data;
};
return data;
}
function _process() {
const data = require("nuclide-commons/process");
_process = function () {
return data;
};
return data;
}
var _os = _interopRequireDefault(require("os"));
function _fs2() {
const data = _interopRequireDefault(require("../common/fs"));
_fs2 = function () {
return data;
};
return data;
}
function _BigDigServer() {
const data = require("./BigDigServer");
_BigDigServer = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* strict-local
* @format
*/
function main() {
// launchServer should only be spawned from ./main.js.
if (process.send == null) {
// eslint-disable-next-line no-console
console.error('Error: launchServer should only be spawned via parseArgsAndRunMain.');
process.exit(1);
}
process.on('message', params => {
handleLaunchParams(params).catch(error => {
_log4js().default.getLogger().fatal('launchServer failed:', error);
_log4js().default.shutdown(() => process.exit(1));
});
});
}
async function handleLaunchParams(params) {
if (params.exclusive != null) {
await enforceExclusive(params.exclusive);
}
const server = await _BigDigServer().BigDigServer.createServer({
ports: params.ports,
webServer: {
key: params.key,
cert: params.cert,
ca: params.ca
},
absolutePathToServerMain: params.absolutePathToServerMain,
serverParams: params.serverParams
});
const port = server.getPort();
if (!(process.send != null)) {
throw new Error("Invariant violation: \"process.send != null\"");
}
process.send({
port
}, () => {
if (!process.disconnect) {
throw new Error("Invariant violation: \"process.disconnect\"");
}
process.disconnect();
}); // Exit once the certificates expire, as no clients will be able to connect at this point.
setTimeout(() => {
_log4js().default.getLogger().info(`Certificates expired after ${params.expirationDays} days, shutting down.`);
process.exit(2);
}, params.expirationDays * 24 * 60 * 60 * 1000);
} // When an 'exclusive' parameter is provided, we'll ensure that only one server
// with a given "exclusive" tag is alive at any given time (per user).
// We do this by storing a .bigdig.exclusive.pid file in sharedCertsDir:
// if the file already exists, we'll try to kill the PID in that file.
async function enforceExclusive(exclusive) {
const bigDigPath = _nuclideUri().default.join(_os.default.homedir(), '.big-dig');
try {
await _fs2().default.mkdir(bigDigPath);
} catch (err) {
if (err.code !== 'EEXIST') {
throw err;
}
}
const pidFile = _nuclideUri().default.join(bigDigPath, `.big-dig.${exclusive}.pid`);
while (true) {
try {
const c = _fs.default.constants; // O_CREAT / O_EXCL atomically creates the PID file.
// Ideally we'd use fcntl/flock to hold onto the PID file until exit,
// but sadly there's no easy flock API in Node.
const handle = _fs.default.openSync(pidFile, // eslint-disable-next-line no-bitwise
c.O_WRONLY | c.O_CREAT | c.O_EXCL, // Readable only for the current user.
0o600);
_log4js().default.getLogger().info(`Writing pid=${process.pid} to ${pidFile}`); // $FlowFixMe: writeFileSync takes handles too.
_fs.default.writeFileSync(handle, process.pid);
_fs.default.closeSync(handle);
break;
} catch (error) {
if (error.code === 'EEXIST') {
// Note: the read, kill, and unlink steps could all throw.
// However, an exception at any of those steps probably indicates a race,
// in which case we should probably bail out anyway.
const pidContents = _fs.default.readFileSync(pidFile, 'utf8');
const pid = parseInt(pidContents, 10);
if (pid > 0) {
_log4js().default.getLogger().info(`Killing existing server with pid=${pid}`); // Node doesn't have any flock() style primitives, so we can't be certain
// that this pid still corresponds to the process.
// As a quick sanity check, we'll inspect the pstree to see that it's consistent.
// eslint-disable-next-line no-await-in-loop
const processTree = await (0, _process().psTree)();
const processInfo = processTree.find(proc => proc.pid === pid);
if (processInfo != null && processInfo.commandWithArgs.includes('launchServer')) {
process.kill(pid);
}
}
function* (params) {
if (params.exclusive != null) {
yield enforceExclusive(params.exclusive);
}
const port = yield (0, (_NuclideServer || _load_NuclideServer()).launchServer)({
ports: params.ports,
webServer: {
key: params.key,
cert: params.cert,
ca: params.ca },
absolutePathToServerMain: params.absolutePathToServerMain,
serverParams: params.serverParams });if (!(
process.send != null)) {throw new Error('Invariant violation: "process.send != null"');}
process.send({ port }, function () {if (!
process.disconnect) {throw new Error('Invariant violation: "process.disconnect"');}
process.disconnect();
});
// Exit once the certificates expire, as no clients will be able to connect at this point.
setTimeout(function () {
(_log4js || _load_log4js()).default.
getLogger().
info(
`Certificates expired after ${
params.expirationDays
} days, shutting down.`);
process.exit(2);
}, params.expirationDays * 24 * 60 * 60 * 1000);
});return function handleLaunchParams(_x) {return _ref.apply(this, arguments);};})();
// When an 'exclusive' parameter is provided, we'll ensure that only one server
// with a given "exclusive" tag is alive at any given time (per user).
// We do this by storing a .bigdig.exclusive.pid file in sharedCertsDir:
// if the file already exists, we'll try to kill the PID in that file.
let enforceExclusive = (() => {var _ref2 = (0, _asyncToGenerator.default)(function* (exclusive) {
const bigDigPath = (_nuclideUri || _load_nuclideUri()).default.join(_os.default.homedir(), '.big-dig');
try {
yield (_fs2 || _load_fs()).default.mkdir(bigDigPath);
} catch (err) {
if (err.code !== 'EEXIST') {
throw err;
_fs.default.unlinkSync(pidFile);
} else {
throw error;
}
}
const pidFile = (_nuclideUri || _load_nuclideUri()).default.join(bigDigPath, `.big-dig.${exclusive}.pid`);
} // Attempt to clean up the pid file on graceful exits.
while (true) {
try {
const c = _fs.default.constants;
// O_CREAT / O_EXCL atomically creates the PID file.
// Ideally we'd use fcntl/flock to hold onto the PID file until exit,
// but sadly there's no easy flock API in Node.
const handle = _fs.default.openSync(
pidFile,
// eslint-disable-next-line no-bitwise
c.O_WRONLY | c.O_CREAT | c.O_EXCL,
// Readable only for the current user.
0o600);
(_log4js || _load_log4js()).default.getLogger().info(`Writing pid=${process.pid} to ${pidFile}`);
// $FlowFixMe: writeFileSync takes handles too.
_fs.default.writeFileSync(handle, process.pid);
_fs.default.closeSync(handle);
break;
} catch (error) {
if (error.code === 'EEXIST') {
// Note: the read, kill, and unlink steps could all throw.
// However, an exception at any of those steps probably indicates a race,
// in which case we should probably bail out anyway.
const pidContents = _fs.default.readFileSync(pidFile, 'utf8');
const pid = parseInt(pidContents, 10);
if (pid > 0) {
(_log4js || _load_log4js()).default.getLogger().info(`Killing existing server with pid=${pid}`);
// Node doesn't have any flock() style primitives, so we can't be certain
// that this pid still corresponds to the process.
// As a quick sanity check, we'll inspect the pstree to see that it's consistent.
// eslint-disable-next-line no-await-in-loop
const processTree = yield (0, (_process || _load_process()).psTree)();
const processInfo = processTree.find(function (proc) {return proc.pid === pid;});
if (
processInfo != null &&
processInfo.commandWithArgs.includes('launchServer'))
{
process.kill(pid);
}
}
_fs.default.unlinkSync(pidFile);
} else {
throw error;
}
}
}
process.on('exit', () => {
_fs.default.unlinkSync(pidFile);
});
}
// Attempt to clean up the pid file on graceful exits.
process.on('exit', function () {
_fs.default.unlinkSync(pidFile);
});
});return function enforceExclusive(_x2) {return _ref2.apply(this, arguments);};})();var _fs = _interopRequireDefault(require('fs'));var _log4js;function _load_log4js() {return _log4js = _interopRequireDefault(require('log4js'));}var _nuclideUri;function _load_nuclideUri() {return _nuclideUri = _interopRequireDefault(require('nuclide-commons/nuclideUri'));}var _process;function _load_process() {return _process = require('nuclide-commons/process');}var _os = _interopRequireDefault(require('os'));var _fs2;function _load_fs() {return _fs2 = _interopRequireDefault(require('../common/fs'));}var _NuclideServer;function _load_NuclideServer() {return _NuclideServer = require('./NuclideServer');}function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}function main() {// launchServer should only be spawned from ./main.js.
if (process.send == null) {// eslint-disable-next-line no-console
console.error('Error: launchServer should only be spawned via parseArgsAndRunMain.');process.exit(1);}process.on('message', params => {handleLaunchParams(params).catch(error => {(_log4js || _load_log4js()).default.getLogger().fatal('launchServer failed:', error);(_log4js || _load_log4js()).default.shutdown(() => process.exit(1));});});} /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/(_log4js || _load_log4js()).default.configure({ appenders: [{ type: 'file', filename: (_nuclideUri || _load_nuclideUri()).default.join(_os.default.tmpdir(), 'big-dig.log') }, { type: 'console' }] });
_log4js().default.configure({
appenders: [{
type: 'file',
filename: _nuclideUri().default.join(_os.default.tmpdir(), 'big-dig.log')
}, {
type: 'stderr'
}]
});
process.on('unhandledRejection', error => {
(_log4js || _load_log4js()).default.getLogger().error('Unhandled rejection:', error);
_log4js().default.getLogger().error('Unhandled rejection:', error);
});
process.on('uncaughtException', error => {
_log4js().default.getLogger().fatal('Uncaught exception:', error);
process.on('uncaughtException', error => {
(_log4js || _load_log4js()).default.getLogger().fatal('Uncaught exception:', error);
(_log4js || _load_log4js()).default.shutdown(() => process.abort());
_log4js().default.shutdown(() => process.abort());
});
main();

@@ -1,146 +0,192 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.generateCertificatesAndStartServer = undefined;var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/let generateCertificatesAndStartServer = exports.generateCertificatesAndStartServer = (() => {var _ref = (0, _asyncToGenerator.default)(
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.startServer = startServer;
function _promise() {
const data = require("nuclide-commons/promise");
_promise = function () {
return data;
};
return data;
}
function _fs() {
const data = _interopRequireDefault(require("../common/fs"));
_fs = function () {
return data;
};
return data;
}
var _child_process = _interopRequireDefault(require("child_process"));
function _log4js() {
const data = require("log4js");
_log4js = function () {
return data;
};
return data;
}
var _os = _interopRequireDefault(require("os"));
function _temp() {
const data = _interopRequireDefault(require("temp"));
_temp = function () {
return data;
};
return data;
}
function _certificates() {
const data = require("./certificates");
_certificates = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
async function startServer({
certificateStrategy,
ports,
timeout,
expirationDays,
exclusive,
jsonOutputFile,
absolutePathToServerMain,
serverParams
}) {
const logger = (0, _log4js().getLogger)();
logger.info('in startServer()');
let paths;
let certificateGeneratorOutput = {};
switch (certificateStrategy.type) {
case 'generate':
const {
clientCommonName,
serverCommonName,
openSSLConfigPath
} = certificateStrategy;
paths = await (0, _certificates().generateCertificates)(clientCommonName, serverCommonName, openSSLConfigPath, expirationDays);
logger.info('generateCertificates() succeeded!');
certificateGeneratorOutput = {
hostname: serverCommonName,
cert: await _fs().default.readFileAsString(paths.clientCert),
key: await _fs().default.readFileAsString(paths.clientKey)
};
break;
case 'reuse':
paths = certificateStrategy.paths;
logger.info('reusing existing certificates');
break;
default:
certificateStrategy.type;
throw new Error('invalid certificate strategy');
}
function* ({
clientCommonName,
serverCommonName,
openSSLConfigPath,
const [key, cert, ca] = await Promise.all([_fs().default.readFileAsBuffer(paths.serverKey), _fs().default.readFileAsBuffer(paths.serverCert), _fs().default.readFileAsBuffer(paths.caCert)]);
const params = {
key: key.toString(),
cert: cert.toString(),
ca: ca.toString(),
ports,
timeout,
expirationDays,
exclusive,
jsonOutputFile,
absolutePathToServerMain,
serverParams })
{
const logger = (0, (_log4js || _load_log4js()).getLogger)();
logger.info('in generateCertificatesAndStartServer()');
serverParams
}; // Redirect child stderr to a file so that we can read it.
// (If we just pipe it, there's no safe way of disconnecting it after.)
const paths = yield (0, (_certificates || _load_certificates()).generateCertificates)(
clientCommonName,
serverCommonName,
openSSLConfigPath,
expirationDays);
_temp().default.track();
logger.info('generateCertificates() succeeded!');
const stderrLog = _temp().default.openSync('big-dig-stderr');
const [key, cert, ca] = yield Promise.all([
(_fs || _load_fs()).default.readFileAsBuffer(paths.serverKey),
(_fs || _load_fs()).default.readFileAsBuffer(paths.serverCert),
(_fs || _load_fs()).default.readFileAsBuffer(paths.caCert)]);
const launcherScript = require.resolve("./launchServer-entry.js");
const params = {
key: key.toString(),
cert: cert.toString(),
ca: ca.toString(),
ports,
expirationDays,
exclusive,
absolutePathToServerMain,
serverParams };
logger.info(`About to spawn ${launcherScript} to launch Big Dig server.`);
const child = _child_process.default.spawn(process.execPath, [// Increase stack trace limit for better debug logs.
// For reference, Atom/Electron does not have a stack trace limit.
'--stack-trace-limit=50', // Increase the maximum heap size if we have enough memory.
...(_os.default.totalmem() > 8 * 1024 * 1024 * 1024 ? ['--max-old-space-size=4096'] : []), launcherScript], {
detached: true,
stdio: ['ignore', 'ignore', stderrLog.fd, 'ipc']
});
// Redirect child stderr to a file so that we can read it.
// (If we just pipe it, there's no safe way of disconnecting it after.)
(_temp || _load_temp()).default.track();
const stderrLog = (_temp || _load_temp()).default.openSync('big-dig-stderr');
logger.info(`spawn called for ${launcherScript}`); // Send launch parameters over IPC to avoid making them visible in `ps`.
const launcherScript = require.resolve('./launchServer-entry.js');
logger.info(`About to spawn ${launcherScript} to launch Big Dig server.`);
const child = _child_process.default.spawn(
process.execPath,
[
// Increase stack trace limit for better debug logs.
// For reference, Atom/Electron does not have a stack trace limit.
'--stack-trace-limit=50',
// Increase the maximum heap size if we have enough memory.
...(_os.default.totalmem() > 8 * 1024 * 1024 * 1024 ?
['--max-old-space-size=4096'] :
[]),
launcherScript],
child.send(params);
const childPort = await (0, _promise().timeoutPromise)(new Promise((resolve, reject) => {
const onMessage = ({
port: result
}) => {
resolve(result);
child.removeAllListeners();
};
{
detached: true,
stdio: ['ignore', 'ignore', stderrLog.fd, 'ipc'] });
logger.info(`spawn called for ${launcherScript}`);
// Send launch parameters over IPC to avoid making them visible in `ps`.
child.send(params);
const childPort = yield (0, (_promise || _load_promise()).timeoutPromise)(
new Promise(function (resolve, reject) {
const onMessage = function ({ port: result }) {
resolve(result);
child.removeAllListeners();
};
child.on('message', onMessage);
child.on('error', reject);
child.on('exit', (() => {var _ref2 = (0, _asyncToGenerator.default)(function* (code) {
const stderr = yield (_fs || _load_fs()).default.
readFileAsString(stderrLog.path).
catch(function () {return '';});
reject(
Error(`Child exited early with code ${code}.\nstderr: ${stderr}`));
});return function (_x2) {return _ref2.apply(this, arguments);};})());
}),
timeout).
catch(function (err) {
// Make sure we clean up hung children.
if (err instanceof (_promise || _load_promise()).TimedOutError) {
child.kill('SIGKILL');
}
return Promise.reject(err);
child.on('message', onMessage);
child.on('error', reject);
child.on('exit', async code => {
const stderr = await _fs().default.readFileAsString(stderrLog.path).catch(() => '');
reject(Error(`Child exited early with code ${code}.\nstderr: ${stderr}`));
});
}), timeout).catch(err => {
// Make sure we clean up hung children.
if (err instanceof _promise().TimedOutError) {
child.kill('SIGKILL');
}
const { version } = require('../../package.json');
const json = JSON.stringify(
// These properties are the ones currently written by nuclide-server.
{
pid: process.pid,
version,
hostname: serverCommonName,
port: childPort,
ca: ca.toString(),
cert: yield (_fs || _load_fs()).default.readFileAsString(paths.clientCert),
key: yield (_fs || _load_fs()).default.readFileAsString(paths.clientKey),
success: true });
return Promise.reject(err);
});
const {
version
} = require("../../package.json");
yield (_fs || _load_fs()).default.writeFile(jsonOutputFile, json, { mode: 0o600 });
logger.info(`Server config written to ${jsonOutputFile}.`);
child.unref();
});return function generateCertificatesAndStartServer(_x) {return _ref.apply(this, arguments);};})();var _promise;function _load_promise() {return _promise = require('nuclide-commons/promise');}var _fs;function _load_fs() {return _fs = _interopRequireDefault(require('../common/fs'));}var _child_process = _interopRequireDefault(require('child_process'));var _log4js;function _load_log4js() {return _log4js = require('log4js');}var _os = _interopRequireDefault(require('os'));var _temp;function _load_temp() {return _temp = _interopRequireDefault(require('temp'));}var _certificates;function _load_certificates() {return _certificates = require('./certificates');}function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}
const json = JSON.stringify( // These properties are the ones currently written by nuclide-server.
Object.assign({}, certificateGeneratorOutput, {
pid: child.pid,
version,
port: childPort,
ca: ca.toString(),
ca_path: paths.caCert,
server_cert_path: paths.serverCert,
server_key_path: paths.serverKey,
protocol_version: 2,
success: true
}));
await _fs().default.writeFile(jsonOutputFile, json, {
mode: 0o600
});
logger.info(`Server config written to ${jsonOutputFile}.`);
child.unref();
}

@@ -1,29 +0,30 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.compress = compress;
exports.decompress = decompress;
var _zlib = _interopRequireDefault(require("zlib"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* strict
* @format
*/
function compress(data) {
return _zlib.default.deflateSync(data);
}
compress = compress;exports.
decompress = decompress;var _zlib = _interopRequireDefault(require('zlib'));function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}function compress(data) {return _zlib.default.deflateSync(data);} /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/function decompress(data) {return _zlib.default.inflateSync(data).toString('utf-8');}
function decompress(data) {
return _zlib.default.inflateSync(data).toString('utf-8');
}

@@ -1,478 +0,559 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.QueuedAckTransport = exports.ACK = exports.CONTENT = exports.PENDING_MESSAGE_TIMEOUT = exports.ACK_BUFFER_TIME = undefined;exports.
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.frameContent = frameContent;
exports.frameAck = frameAck;
exports.parseMessage = parseMessage;
exports.QueuedAckTransport = exports.ACK = exports.CONTENT = exports.PENDING_MESSAGE_TIMEOUT = exports.ACK_BUFFER_TIME = void 0;
function _doubleEndedQueue() {
const data = _interopRequireDefault(require("double-ended-queue"));
_doubleEndedQueue = function () {
return data;
};
return data;
}
var _RxMin = require("rxjs/bundles/Rx.min.js");
function _log4js() {
const data = require("log4js");
_log4js = function () {
return data;
};
return data;
}
function _eventKit() {
const data = require("event-kit");
_eventKit = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
const logger = (0, _log4js().getLogger)('reliable-socket');
const ACK_BUFFER_TIME = 100;
exports.ACK_BUFFER_TIME = ACK_BUFFER_TIME;
const PENDING_MESSAGE_TIMEOUT = 30 * 1000;
exports.PENDING_MESSAGE_TIMEOUT = PENDING_MESSAGE_TIMEOUT;
const CONTENT = 'CONTENT';
exports.CONTENT = CONTENT;
const ACK = 'ACK';
exports.ACK = ACK;
// Adapter to make an UnreliableTransport a reliable Transport
// by queuing messages and removing from the queue only after
// receiving an ack that the recipient has received it.
//
// Conforms to the RPC Framework's Transport type.
//
// Must be constructed with an open(not closed) transport.
// Can be in one of 3 states: open, disconnected, or closed.
// The transport starts in open state. When the current transport closes,
// goes to disconnected state.
// While disconnected, reconnect can be called to return to the open state.
// close() closes the underlying transport and transitions to closed state.
// Once closed, reconnect may not be called and no other events will be emitted.
class QueuedAckTransport {
constructor(clientId, transport, protocolLogger) {
this._lastSendId = 0;
this._lastProcessedId = 0;
this.id = clientId;
this._isClosed = false;
this._transport = null;
this._pendingSends = new (_doubleEndedQueue().default)();
this._pendingReceives = new Map();
this._messageProcessor = new _RxMin.Subject();
this._emitter = new (_eventKit().Emitter)();
this._protocolLogger = protocolLogger;
if (transport != null) {
this._connect(transport);
}
}
getState() {
this._checkLeaks();
return this._isClosed ? 'closed' : this._transport == null ? 'disconnected' : 'open';
}
onDisconnect(callback) {
return this._emitter.on('disconnect', callback);
}
onMessage() {
return this._messageProcessor;
}
_connect(transport) {
this._logInfo(`${this.id} connect`);
if (!!transport.isClosed()) {
throw new Error('connect with closed transport');
}
if (!(this._transport == null)) {
throw new Error('connect with existing this._transport');
}
this._transport = transport;
transport.onMessage().subscribe(this._handleMessage.bind(this));
transport.onClose(() => this._handleTransportClose(transport));
}
_handleTransportClose(transport) {
if (!transport.isClosed()) {
throw new Error('handleTransportClose transport is closed');
}
if (this._isClosed) {
// This happens when close() is called and we have an open transport.
this._logInfo(`${this.id} handleTransportClose (but already closed)`);
} else if (transport !== this._transport) {
// This should not happen, but we don't care enough to track.
this._logError(`${this.id} handleTransportClose (but unexpected transport)`);
} else {
this._logInfo(`${this.id} handleTransportClose`);
this._transport = null;
this._cancelPendingMessageTimer();
this._cancelAckTimer();
this._emitter.emit('disconnect', transport);
}
this._checkLeaks();
}
reconnect(transport) {
if (this._isClosed) {
this._logInfo(`${this.id} reconnect (but already closed)`);
this._checkLeaks();
return;
}
if (!!transport.isClosed()) {
throw new Error('reconnect with closed transport');
}
this._logInfo(`${this.id} reconnect (${this._pendingSends.length} sends, ${this._pendingReceives.size} receives)`);
if (this._transport != null) {
this._transport.close();
}
this._connect(transport);
this._resendQueue();
this._checkLeaks();
}
disconnect(caller = 'external') {
this._logTrace(`${this.id} disconnect (caller=${caller}, state=${this.getState()}))`);
const transport = this._transport;
if (transport != null) {
if (!!this._isClosed) {
throw new Error("Invariant violation: \"!this._isClosed\"");
}
transport.close();
}
if (!(this._transport == null)) {
throw new Error("Invariant violation: \"this._transport == null\"");
}
this._checkLeaks();
}
send(message) {
if (this._isClosed) {
this._logTrace(`${this.id} send (but already closed) '${message}'`);
this._checkLeaks();
return;
}
const id = ++this._lastSendId;
const wireMessage = frameContent(id, message);
this._pendingSends.enqueue({
id,
wireMessage
});
this._transportSend(wireMessage);
this._maybeStartPendingMessageTimer();
this._checkLeaks();
}
_resendQueue() {
this._logInfo(`${this.id} resendQueue`);
this._sendAck();
this._pendingSends.toArray().forEach(x => this._transportSend(x.wireMessage), this);
this._maybeStartPendingMessageTimer();
}
_handleMessage(wireMessage) {
if (this._isClosed) {
this._logTrace(`${this.id} receive (but already closed) '${wireMessage}'`);
this._checkLeaks();
return;
}
const parsed = parseMessage(wireMessage);
let progress = 0;
switch (parsed.type) {
case CONTENT:
{
this._logTrace(`${this.id} received ${_forLogging(wireMessage)}`);
const pending = this._pendingReceives; // If this is a repeat of an old message, don't add it, since we
// only remove messages when we process them.
if (parsed.id > this._lastProcessedId) {
pending.set(parsed.id, parsed.message);
}
while (true) {
const id = this._lastProcessedId + 1;
const message = pending.get(id);
if (message == null) {
break;
}
this._messageProcessor.next(message);
pending.delete(id);
this._lastProcessedId = id;
progress++;
}
if (progress !== 1) {
this._logTrace(`${this.id} processed ${progress} messages`);
}
this._ensureAckTimer();
break;
}
case ACK:
{
const pending = this._pendingSends;
const id = parsed.id;
if (id > this._lastSendId) {
// The id needs to be smaller than or equal to the _lastSendId unless
// the client is reconnecting after a close (which can happen in a
// specific client-side race condition). The invariant here makes
// sure this is the case.
if (!(this._lastSendId === 0 && this._lastProcessedId === 0)) {
throw new Error("Invariant violation: \"this._lastSendId === 0 && this._lastProcessedId === 0\"");
}
this.close();
break;
} else {
while (true) {
const front = pending.peekFront();
if (front == null || front.id > id) {
break;
}
pending.dequeue();
progress++;
}
this._logTrace(`${this.id} received ack ${wireMessage} (cleared ${progress} messages, last sent ${this._lastSendId})`);
}
break;
}
} // Note that this only restarts the timer if (a) we still have something
// pending, and (b) we made progress here and canceled the existing timer.
// If wireMessage did not actually move us forward, we did not cancel the
// existing timer so _maybeStartPendingMessageTimer will be a no-op.
if (progress > 0) {
this._cancelPendingMessageTimer();
}
this._maybeStartPendingMessageTimer();
this._checkLeaks();
}
close() {
if (!this._isClosed) {
this._logTrace(`${this.id} close`);
this.disconnect('close');
this._pendingSends.clear();
this._pendingReceives.clear();
this._isClosed = true;
} else {
this._logTrace(`${this.id} close (but already closed)`);
}
this._checkLeaks();
}
isClosed() {
this._checkLeaks();
return this._isClosed;
}
_ensureAckTimer() {
if (!this._isClosed && this._transport != null && this._ackTimer == null) {
this._ackTimer = setTimeout(this._sendAck.bind(this), ACK_BUFFER_TIME);
}
}
_cancelAckTimer() {
if (this._ackTimer != null) {
clearTimeout(this._ackTimer);
this._ackTimer = null;
}
}
_sendAck() {
this._cancelAckTimer();
if (this._lastProcessedId > 0) {
this._transportSend(frameAck(this._lastProcessedId)); // It seems that a bug in Electron's Node integration can cause ACKs
// to become stuck in the Node event loop indefinitely
// (as they are scheduled using Chromium's setTimeout).
// See T27348369 for more details.
if (process.platform === 'win32' && // $FlowFixMe(>=0.68.0) Flow suppress (T27187857)
typeof process.activateUvLoop === 'function') {
process.activateUvLoop();
}
}
} // If we have a pending send or receive and wait a while without
// an ack or processing a message, disconnect. This should trigger
// ReliableSocket on the client to attempt to reconnect.
_maybeStartPendingMessageTimer() {
if (this._pendingMessageTimer == null && this._wantsPendingMessageTimer()) {
this._pendingMessageTimer = setTimeout(this._handlePendingMessageTimeout.bind(this), PENDING_MESSAGE_TIMEOUT);
}
}
_cancelPendingMessageTimer() {
if (this._pendingMessageTimer != null) {
clearTimeout(this._pendingMessageTimer);
this._pendingMessageTimer = null;
}
}
_handlePendingMessageTimeout() {
if (!!this._isClosed) {
throw new Error('isClosed');
}
if (!(this._transport != null)) {
throw new Error('transport');
}
if (!this._hasPendingMessage()) {
throw new Error('hasPendingMessage');
}
this.disconnect('timeout');
}
_wantsPendingMessageTimer() {
return !this._isClosed && this._transport != null && this._hasPendingMessage();
}
_hasPendingMessage() {
return !this._pendingSends.isEmpty() || this._pendingReceives.size > 0;
}
_transportSend(wireMessage) {
const transport = this._transport;
const summary = _forLogging(wireMessage);
if (transport != null) {
this._logTrace(`${this.id} transport send ${summary}`);
transport.send(wireMessage);
} else {
this._logTrace(`${this.id} transport send (but disconnected) ${summary}`);
}
}
_checkLeaks() {
if (this._isClosed) {
if (!this._pendingSends.isEmpty()) {
throw new Error('pendingSends');
}
if (!(this._pendingReceives.size === 0)) {
throw new Error('pendingReceives');
}
if (!(this._transport == null)) {
throw new Error('transport');
}
}
if (this._transport == null) {
if (!(this._ackTimer == null)) {
throw new Error('ackTimer');
}
if (!(this._pendingMessageTimer == null)) {
throw new Error('pendingMessageTimer');
}
}
} // Helper functions to log sufficiently interesting logs to both
// logger (disk) and protocolLogger (circular in-memory).
_logError(format, ...args) {
logger.error(format, ...args);
if (this._protocolLogger != null) {
this._protocolLogger.error(format, ...args);
}
}
_logInfo(format, ...args) {
logger.info(format, ...args);
if (this._protocolLogger != null) {
this._protocolLogger.info(format, ...args);
}
}
_logTrace(format, ...args) {
if (this._protocolLogger != null) {
this._protocolLogger.trace(format, ...args);
}
}
} // exported for testing
exports.QueuedAckTransport = QueuedAckTransport;
function frameContent(id, message) {
return `>${id}:${message}`;
} // exported for testing
function frameAck(id) {
return `<${id}:`;
} // exported for testing
function parseMessage(wireMessage) {
const iColon = wireMessage.indexOf(':');
if (!(iColon !== -1)) {
throw new Error("Invariant violation: \"iColon !== -1\"");
}
const mode = wireMessage[0];
const id = Number(wireMessage.substring(1, iColon));
const message = wireMessage.substring(iColon + 1);
if (mode === '>') {
return {
type: CONTENT,
id,
message
};
} else if (mode === '<') {
return {
type: ACK,
id
};
} else {
if (!false) {
throw new Error(`Unrecognized mode in wire message '${wireMessage}'`);
}
}
}
const MAX_RAW_LOG = 256;
const PROTOCOL_COMMON = '"protocol":"service_framework3_rpc"';
function _forLogging(message) {
const truncated = message.substr(0, MAX_RAW_LOG);
const noUserInput = removeUserInput(truncated);
const noProtocol = noUserInput.replace(PROTOCOL_COMMON, '..');
const ellipsed = message.length > MAX_RAW_LOG && !noProtocol.endsWith('..') ? noProtocol + '..' : noProtocol;
return JSON.stringify(ellipsed);
}
const WRITE_INPUT = '"method":"writeInput"';
const WRITE_INPUT_DATA_PREFIX = '"args":{"data":';
function removeUserInput(message) {
const methodIndex = message.indexOf(WRITE_INPUT);
if (methodIndex < 0) {
return message;
}
const argsIndex = message.indexOf(WRITE_INPUT_DATA_PREFIX, methodIndex);
if (argsIndex < 0) {
return message;
}
frameContent = frameContent;exports.
frameAck = frameAck;exports.
parseMessage = parseMessage;var _doubleEndedQueue;function _load_doubleEndedQueue() {return _doubleEndedQueue = _interopRequireDefault(require('double-ended-queue'));}var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js');var _log4js;function _load_log4js() {return _log4js = require('log4js');}var _eventKit;function _load_eventKit() {return _eventKit = require('event-kit');}function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}const logger = (0, (_log4js || _load_log4js()).getLogger)('nuclide-server'); /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/const ACK_BUFFER_TIME = exports.ACK_BUFFER_TIME = 100;const PENDING_MESSAGE_TIMEOUT = exports.PENDING_MESSAGE_TIMEOUT = 30 * 1000;const CONTENT = exports.CONTENT = 'CONTENT';const ACK = exports.ACK = 'ACK'; // Adapter to make an UnreliableTransport a reliable Transport
// by queuing messages and removing from the queue only after
// receiving an ack that the recipient has received it.
//
// Conforms to the RPC Framework's Transport type.
//
// Must be constructed with an open(not closed) transport.
// Can be in one of 3 states: open, disconnected, or closed.
// The transport starts in open state. When the current transport closes,
// goes to disconnected state.
// While disconnected, reconnect can be called to return to the open state.
// close() closes the underlying transport and transitions to closed state.
// Once closed, reconnect may not be called and no other events will be emitted.
class QueuedAckTransport {constructor(clientId, transport, protocolLogger) {this._lastSendId = 0;this._lastProcessedId = 0;this.id = clientId;this._isClosed = false;this._transport = null;this._pendingSends = new (_doubleEndedQueue || _load_doubleEndedQueue()).default();this._pendingReceives = new Map();this._messageProcessor = new _rxjsBundlesRxMinJs.Subject();this._emitter = new (_eventKit || _load_eventKit()).Emitter();this._protocolLogger = protocolLogger;if (transport != null) {this._connect(transport);}}getState() {this._checkLeaks();return this._isClosed ? 'closed' : this._transport == null ? 'disconnected' : 'open';}onDisconnect(callback) {return this._emitter.on('disconnect', callback);}onMessage() {return this._messageProcessor;}_connect(transport) {this._logInfo(`${this.id} connect`);if (!!transport.isClosed()) {throw new Error('connect with closed transport');}if (!(this._transport == null)) {throw new Error('connect with existing this._transport');}this._transport = transport;transport.onMessage().subscribe(this._handleMessage.bind(this));transport.onClose(() => this._handleTransportClose(transport));}_handleTransportClose(transport) {if (!transport.isClosed()) {throw new Error('handleTransportClose transport is closed');}if (this._isClosed) {// This happens when close() is called and we have an open transport.
this._logInfo(`${this.id} handleTransportClose (but already closed)`);} else if (transport !== this._transport) {// This should not happen, but we don't care enough to track.
this._logError(`${this.id} handleTransportClose (but unexpected transport)`);} else {this._logInfo(`${this.id} handleTransportClose`);this._transport = null;this._cancelPendingMessageTimer();this._cancelAckTimer();this._emitter.emit('disconnect', transport);}this._checkLeaks();}reconnect(transport) {if (this._isClosed) {this._logInfo(`${this.id} reconnect (but already closed)`);this._checkLeaks();return;}if (!!transport.isClosed()) {throw new Error('reconnect with closed transport');}this._logInfo(`${this.id} reconnect (${this._pendingSends.length} sends, ${this._pendingReceives.size} receives)`);if (this._transport != null) {this._transport.close();}this._connect(transport);this._resendQueue();this._checkLeaks();}disconnect(caller = 'external') {this._logTrace(`${this.id} disconnect (caller=${caller}, state=${this.getState()}))`);const transport = this._transport;if (transport != null) {if (!!this._isClosed) {throw new Error('Invariant violation: "!this._isClosed"');}transport.close();}if (!(this._transport == null)) {throw new Error('Invariant violation: "this._transport == null"');}this._checkLeaks();}send(message) {if (this._isClosed) {this._logTrace(`${this.id} send (but already closed) '${message}'`);this._checkLeaks();return;}const id = ++this._lastSendId;const wireMessage = frameContent(id, message);this._pendingSends.enqueue({ id, wireMessage });this._transportSend(wireMessage);this._maybeStartPendingMessageTimer();this._checkLeaks();}_resendQueue() {this._logInfo(`${this.id} resendQueue`);this._sendAck();this._pendingSends.toArray().forEach(x => this._transportSend(x.wireMessage), this);this._maybeStartPendingMessageTimer();}_handleMessage(wireMessage) {if (this._isClosed) {this._logTrace(`${this.id} receive (but already closed) '${wireMessage}'`);this._checkLeaks();return;}const parsed = parseMessage(wireMessage);let progress = 0;switch (parsed.type) {case CONTENT:{this._logTrace(`${this.id} received ${_forLogging(wireMessage)}`);const pending = this._pendingReceives; // If this is a repeat of an old message, don't add it, since we
// only remove messages when we process them.
if (parsed.id > this._lastProcessedId) {pending.set(parsed.id, parsed.message);}while (true) {const id = this._lastProcessedId + 1;const message = pending.get(id);if (message == null) {break;}this._messageProcessor.next(message);pending.delete(id);this._lastProcessedId = id;progress++;}if (progress !== 1) {this._logTrace(`${this.id} processed ${progress} messages`);}this._ensureAckTimer();break;}case ACK:{const pending = this._pendingSends;const id = parsed.id;if (id > this._lastSendId) {// The id needs to be smaller than or equal to the _lastSendId unless
// the client is reconnecting after a close (which can happen in a
// specific client-side race condition). The invariant here makes
// sure this is the case.
if (!(this._lastSendId === 0 && this._lastProcessedId === 0)) {throw new Error('Invariant violation: "this._lastSendId === 0 && this._lastProcessedId === 0"');}this.close();break;} else {while (true) {const front = pending.peekFront();if (front == null || front.id > id) {break;}pending.dequeue();progress++;}this._logTrace(`${this.id} received ack ${wireMessage} (cleared ${progress} messages, last sent ${this._lastSendId})`);}break;}} // Note that this only restarts the timer if (a) we still have something
// pending, and (b) we made progress here and canceled the existing timer.
// If wireMessage did not actually move us forward, we did not cancel the
// existing timer so _maybeStartPendingMessageTimer will be a no-op.
if (progress > 0) {this._cancelPendingMessageTimer();}this._maybeStartPendingMessageTimer();this._checkLeaks();}close() {if (!this._isClosed) {this._logTrace(`${this.id} close`);this.disconnect('close');this._pendingSends.clear();this._pendingReceives.clear();this._isClosed = true;} else {this._logTrace(`${this.id} close (but already closed)`);}this._checkLeaks();}isClosed() {this._checkLeaks();return this._isClosed;}_ensureAckTimer() {if (!this._isClosed && this._transport != null && this._ackTimer == null) {this._ackTimer = setTimeout(this._sendAck.bind(this), ACK_BUFFER_TIME);}}_cancelAckTimer() {if (this._ackTimer != null) {clearTimeout(this._ackTimer);this._ackTimer = null;}}_sendAck() {this._cancelAckTimer();if (this._lastProcessedId > 0) {this._transportSend(frameAck(this._lastProcessedId)); // It seems that a bug in Electron's Node integration can cause ACKs
// to become stuck in the Node event loop indefinitely
// (as they are scheduled using Chromium's setTimeout).
// See T27348369 for more details.
if (process.platform === 'win32' && // $FlowFixMe(>=0.68.0) Flow suppress (T27187857)
typeof process.activateUvLoop === 'function') {process.activateUvLoop();}}} // If we have a pending send or receive and wait a while without
// an ack or processing a message, disconnect. This should trigger
// ReliableSocket on the client to attempt to reconnect.
_maybeStartPendingMessageTimer() {if (this._pendingMessageTimer == null && this._wantsPendingMessageTimer()) {this._pendingMessageTimer = setTimeout(this._handlePendingMessageTimeout.bind(this), PENDING_MESSAGE_TIMEOUT);}}_cancelPendingMessageTimer() {if (this._pendingMessageTimer != null) {clearTimeout(this._pendingMessageTimer);this._pendingMessageTimer = null;}}_handlePendingMessageTimeout() {if (!!this._isClosed) {throw new Error('isClosed');}if (!(this._transport != null)) {throw new Error('transport');}if (!this._hasPendingMessage()) {throw new Error('hasPendingMessage');}this.disconnect('timeout');}_wantsPendingMessageTimer() {return !this._isClosed && this._transport != null && this._hasPendingMessage();}_hasPendingMessage() {return !this._pendingSends.isEmpty() || this._pendingReceives.size > 0;}_transportSend(wireMessage) {const transport = this._transport;const summary = _forLogging(wireMessage);if (transport != null) {this._logTrace(`${this.id} transport send ${summary}`);transport.send(wireMessage);} else {this._logTrace(`${this.id} transport send (but disconnected) ${summary}`);}}_checkLeaks() {if (this._isClosed) {if (!this._pendingSends.isEmpty()) {throw new Error('pendingSends');}if (!(this._pendingReceives.size === 0)) {throw new Error('pendingReceives');}if (!(this._transport == null)) {throw new Error('transport');}}if (this._transport == null) {if (!(this._ackTimer == null)) {throw new Error('ackTimer');}if (!(this._pendingMessageTimer == null)) {throw new Error('pendingMessageTimer');}}} // Helper functions to log sufficiently interesting logs to both
// logger (disk) and protocolLogger (circular in-memory).
_logError(format, ...args) {logger.error(format, ...args);if (this._protocolLogger != null) {this._protocolLogger.error(format, ...args);}}_logInfo(format, ...args) {logger.info(format, ...args);if (this._protocolLogger != null) {this._protocolLogger.info(format, ...args);}}_logTrace(format, ...args) {if (this._protocolLogger != null) {this._protocolLogger.trace(format, ...args);}}}exports.QueuedAckTransport = QueuedAckTransport; // exported for testing
function frameContent(id, message) {return `>${id}:${message}`;} // exported for testing
function frameAck(id) {return `<${id}:`;} // exported for testing
function parseMessage(wireMessage) {const iColon = wireMessage.indexOf(':');if (!(iColon !== -1)) {throw new Error('Invariant violation: "iColon !== -1"');}const mode = wireMessage[0];const id = Number(wireMessage.substring(1, iColon));const message = wireMessage.substring(iColon + 1);if (mode === '>') {return { type: CONTENT, id, message };} else if (mode === '<') {return { type: ACK, id };} else {if (!false) {throw new Error(`Unrecognized mode in wire message '${wireMessage}'`);}}}const MAX_RAW_LOG = 256;const PROTOCOL_COMMON = '"protocol":"service_framework3_rpc"';function _forLogging(message) {const truncated = message.substr(0, MAX_RAW_LOG);const noUserInput = removeUserInput(truncated);const noProtocol = noUserInput.replace(PROTOCOL_COMMON, '..');const ellipsed = message.length > MAX_RAW_LOG && !noProtocol.endsWith('..') ? noProtocol + '..' : noProtocol;return JSON.stringify(ellipsed);}const WRITE_INPUT = '"method":"writeInput"';const WRITE_INPUT_DATA_PREFIX = '"args":{"data":';function removeUserInput(message) {const methodIndex = message.indexOf(WRITE_INPUT);if (methodIndex < 0) {return message;}const argsIndex = message.indexOf(WRITE_INPUT_DATA_PREFIX, methodIndex);if (argsIndex < 0) {return message;}return message.substring(0, argsIndex + WRITE_INPUT_DATA_PREFIX.length) + '<omitted user input>..';}
return message.substring(0, argsIndex + WRITE_INPUT_DATA_PREFIX.length) + '<omitted user input>..';
}

@@ -1,38 +0,100 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.ReliableSocket = undefined;var _asyncToGenerator = _interopRequireDefault(require('async-to-generator'));
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ReliableSocket = void 0;
var _RxMin = require("rxjs/bundles/Rx.min.js");
var _url = _interopRequireDefault(require("url"));
function _ws() {
const data = _interopRequireDefault(require("ws"));
_ws = function () {
return data;
};
return data;
}
function _uuid() {
const data = _interopRequireDefault(require("uuid"));
_uuid = function () {
return data;
};
return data;
}
function _eventKit() {
const data = require("event-kit");
_eventKit = function () {
return data;
};
return data;
}
function _WebSocketTransport() {
const data = require("./WebSocketTransport");
_WebSocketTransport = function () {
return data;
};
var _url = _interopRequireDefault(require('url'));var _ws;
function _load_ws() {return _ws = _interopRequireDefault(require('ws'));}var _uuid;
function _load_uuid() {return _uuid = _interopRequireDefault(require('uuid'));}var _eventKit;
function _load_eventKit() {return _eventKit = require('event-kit');}var _WebSocketTransport;
function _load_WebSocketTransport() {return _WebSocketTransport = require('./WebSocketTransport');}var _QueuedAckTransport;
function _load_QueuedAckTransport() {return _QueuedAckTransport = require('./QueuedAckTransport');}var _XhrConnectionHeartbeat;
function _load_XhrConnectionHeartbeat() {return _XhrConnectionHeartbeat = require('../client/XhrConnectionHeartbeat');}var _log4js;
return data;
}
function _load_log4js() {return _log4js = require('log4js');}function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}
function _QueuedAckTransport() {
const data = require("./QueuedAckTransport");
const logger = (0, (_log4js || _load_log4js()).getLogger)('nuclide-socket'); /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/const PING_SEND_INTERVAL = 5000;const PING_WAIT_INTERVAL = 5000;const INITIAL_RECONNECT_TIME_MS = 10;const MAX_RECONNECT_TIME_MS = 5000; // The Nuclide Socket class does several things:
_QueuedAckTransport = function () {
return data;
};
return data;
}
function _XhrConnectionHeartbeat() {
const data = require("../client/XhrConnectionHeartbeat");
_XhrConnectionHeartbeat = function () {
return data;
};
return data;
}
function _log4js() {
const data = require("log4js");
_log4js = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* strict-local
* @format
*/
const logger = (0, _log4js().getLogger)('reliable-socket');
const PING_SEND_INTERVAL = 5000;
const PING_WAIT_INTERVAL = 5000;
const INITIAL_RECONNECT_TIME_MS = 10;
const MAX_RECONNECT_TIME_MS = 5000; // The ReliableSocket class does several things:
// - Provides a transport mechanism for sending/receiving JSON messages

@@ -46,3 +108,3 @@ // - Provides a transport layer for xhr requests

// - Disconnected - Was connected, but connection died. Will attempt to reconnect.
// - Closed - No longer connected. May not send/receive messages. Cannot be resurected.
// - Closed - No longer connected. May not send/receive messages. Cannot be resurrected.
//

@@ -56,26 +118,14 @@ // Publishes the following events:

// - heartbeat.error({code, originalCode, message}): On failure of heartbeat
// - intransient-error: the server is reachable but refusing to respond to
// connections (i.e. ECONNREFUSED).
// - close: this socket has been closed by a call to `close()`.
class ReliableSocket {
// ID from a setTimeout() call.
constructor(
serverUri,
heartbeatChannel,
options,
protocolLogger)
{
this._emitter = new (_eventKit || _load_eventKit()).Emitter();
constructor(serverUri, heartbeatChannel, options, protocolLogger) {
this._emitter = new (_eventKit().Emitter)();
this._serverUri = serverUri;
this._options = options;
this._heartbeatChannel = heartbeatChannel;
this.id = (_uuid || _load_uuid()).default.v4();
this.id = _uuid().default.v4();
this._pingTimer = null;

@@ -85,3 +135,3 @@ this._reconnectTime = INITIAL_RECONNECT_TIME_MS;

this._previouslyConnected = false;
this._transport = new (_QueuedAckTransport || _load_QueuedAckTransport()).QueuedAckTransport(this.id, null, protocolLogger);
this._transport = new (_QueuedAckTransport().QueuedAckTransport)(this.id, null, protocolLogger);

@@ -91,3 +141,5 @@ this._transport.onDisconnect(() => {

this._emitter.emit('status', false);
this._emitter.emit('disconnect');
this._scheduleReconnect();

@@ -97,16 +149,17 @@ }

const { protocol, host, path } = _url.default.parse(serverUri);if (!(
host != null)) {throw new Error('Invariant violation: "host != null"');}
const {
protocol,
host,
path
} = _url.default.parse(serverUri);
if (!(host != null)) {
throw new Error("Invariant violation: \"host != null\"");
}
const pathString = path != null ? path : '';
this._websocketUri = `ws${
protocol === 'https:' ? 's' : ''
}://${host}${pathString}`;
this._websocketUri = `ws${protocol === 'https:' ? 's' : ''}://${host}${pathString}`;
logger.info(`websocket uri: ${this._websocketUri}`);
this._heartbeat = new (_XhrConnectionHeartbeat().XhrConnectionHeartbeat)(serverUri, this._heartbeatChannel, options);
this._heartbeat = new (_XhrConnectionHeartbeat || _load_XhrConnectionHeartbeat()).XhrConnectionHeartbeat(
serverUri,
this._heartbeatChannel,
options);
this._heartbeat.onConnectionRestored(() => {

@@ -130,5 +183,3 @@ if (this.isDisconnected()) {

isDisconnected() {
return (
this._transport != null && this._transport.getState() === 'disconnected');
return this._transport != null && this._transport.getState() === 'disconnected';
}

@@ -147,17 +198,18 @@

_reconnect() {var _this = this;if (!
this.isDisconnected()) {throw new Error('Invariant violation: "this.isDisconnected()"');}
_reconnect() {
if (!this.isDisconnected()) {
throw new Error("Invariant violation: \"this.isDisconnected()\"");
}
const websocket = new (_ws || _load_ws()).default(this._websocketUri, Object.assign({},
this._options, {
const websocket = new (_ws().default)(this._websocketUri, Object.assign({}, this._options, {
headers: {
client_id: this.id } }));
// Need to add this otherwise unhandled errors during startup will result
client_id: this.id
}
})); // Need to add this otherwise unhandled errors during startup will result
// in uncaught exceptions. This is due to EventEmitter treating 'error'
// events specially.
const onSocketError = error => {
logger.warn(`WebSocket Error while connecting... ${error.message}`);
if (error.code === 'ECONNREFUSED') {

@@ -169,45 +221,62 @@ // Error: "Connection Refused"

}
if (this.isDisconnected()) {
logger.info('WebSocket reconnecting after error.');
this._scheduleReconnect();
}
};
websocket.on('error', onSocketError);
const onSocketOpen = (() => {var _ref = (0, _asyncToGenerator.default)(function* () {
if (_this.isDisconnected()) {
const ws = new (_WebSocketTransport || _load_WebSocketTransport()).WebSocketTransport(_this.id, websocket);
const pingId = (_uuid || _load_uuid()).default.v4();
ws.onClose(function () {
_this._clearPingTimer();
});
ws.onError(function (error) {
ws.close();
});
ws.onPong(function (data) {
if (pingId === data) {
_this._schedulePing(pingId, ws);
} else {
logger.error('pingId mismatch');
}
});
ws.onMessage().subscribe(function () {
_this._schedulePing(pingId, ws);
});
_this._schedulePing(pingId, ws);if (!(
_this._transport != null)) {throw new Error('Invariant violation: "this._transport != null"');}
_this._transport.reconnect(ws);
websocket.removeListener('error', onSocketError);
_this._emitter.emit('status', true);
if (_this._previouslyConnected) {
logger.info('WebSocket reconnected');
_this._emitter.emit('reconnect');
const onSocketOpen = async () => {
if (this.isDisconnected()) {
const ws = new (_WebSocketTransport().WebSocketTransport)(this.id, websocket);
const pingId = _uuid().default.v4();
ws.onClose(() => {
this._clearPingTimer();
});
ws.onError(error => {
ws.close();
});
ws.onPong(data => {
if (pingId === data) {
this._schedulePing(pingId, ws);
} else {
logger.info('WebSocket connected');
_this._emitter.emit('connect');
logger.error('pingId mismatch');
}
_this._previouslyConnected = true;
_this._reconnectTime = INITIAL_RECONNECT_TIME_MS;
});
ws.onMessage().subscribe(() => {
this._schedulePing(pingId, ws);
});
this._schedulePing(pingId, ws);
if (!(this._transport != null)) {
throw new Error("Invariant violation: \"this._transport != null\"");
}
});return function onSocketOpen() {return _ref.apply(this, arguments);};})();
this._transport.reconnect(ws);
websocket.removeListener('error', onSocketError);
this._emitter.emit('status', true);
if (this._previouslyConnected) {
logger.info('WebSocket reconnected');
this._emitter.emit('reconnect');
} else {
logger.info('WebSocket connected');
this._emitter.emit('connect');
}
this._previouslyConnected = true;
this._reconnectTime = INITIAL_RECONNECT_TIME_MS;
}
};
websocket.on('open', onSocketOpen);

@@ -218,2 +287,3 @@ }

this._clearPingTimer();
this._pingTimer = setTimeout(() => {

@@ -238,6 +308,8 @@ ws.ping(data);

return;
}
// Exponential reconnect time trials.
} // Exponential reconnect time trials.
this._reconnectTimer = setTimeout(() => {
this._reconnectTimer = null;
if (this.isDisconnected()) {

@@ -248,2 +320,3 @@ this._reconnect();

this._reconnectTime = this._reconnectTime * 2;
if (this._reconnectTime > MAX_RECONNECT_TIME_MS) {

@@ -261,9 +334,13 @@ this._reconnectTime = MAX_RECONNECT_TIME_MS;

send(message) {if (!(
this._transport != null)) {throw new Error('Invariant violation: "this._transport != null"');}
send(message) {
// "this.isClosed()" but flow understands it
if (this._transport == null) {
throw new Error(`Sending message to server ${this._serverUri} on closed socket ${this.id}: ${message}`);
}
this._transport.send(message);
}
} // Resolves if the connection looks healthy.
// Will reject quickly if the connection looks unhealthy.
// Resolves if the connection looks healthy.
// Will reject quickly if the connection looks unhealthy.
testConnection() {

@@ -282,6 +359,10 @@ return this._heartbeat.sendHeartBeat();

getServerPort() {
const { port } = _url.default.parse(this.getServerUri());
const {
port
} = _url.default.parse(this.getServerUri());
if (port == null) {
return null;
}
return Number(port);

@@ -292,9 +373,14 @@ }

const transport = this._transport;
if (transport != null) {
this._transport = null;
transport.close();
this._emitter.emit('close');
}
this._clearReconnectTimer();
this._reconnectTime = INITIAL_RECONNECT_TIME_MS;
this._heartbeat.close();

@@ -307,4 +393,11 @@ }

onMessage() {if (!(
this._transport != null)) {throw new Error('Invariant violation: "this._transport != null"');}
onMessage() {
if (this.isClosed()) {
return _RxMin.Observable.throw(`Socket ${this.id} to server ${this._serverUri} is closed`);
}
if (!(this._transport != null)) {
throw new Error("Invariant violation: \"this._transport != null\"");
}
return this._transport.onMessage();

@@ -328,18 +421,24 @@ }

}
/**
* Called if there is an intransient error. I.e. when we cannot recover from
* an error by attempting to reconnect. It is up to the listener to decide
* whether to close this socket.
*/
/**
* Called if there is an intransient error. I.e. when we cannot recover from
* an error by attempting to reconnect. It is up to the listener to decide
* whether to close this socket.
*/
onIntransientError(callback) {
return this._emitter.on('intransient-error', callback);
}
/**
* Called just once if the state of this socket goes from opened to closed.
* E.g. this socket is closed via its `close` method.
*/
/**
* Called just once if the state of this socket goes from opened to closed.
* E.g. this socket is closed via its `close` method.
*/
onClose(callback) {
return this._emitter.on('close', callback);
}}exports.ReliableSocket = ReliableSocket;
}
}
exports.ReliableSocket = ReliableSocket;

@@ -1,34 +0,55 @@

'use strict';Object.defineProperty(exports, "__esModule", { value: true });exports.WebSocketTransport = undefined;
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.WebSocketTransport = void 0;
var _RxMin = require("rxjs/bundles/Rx.min.js");
function _log4js() {
const data = require("log4js");
_log4js = function () {
return data;
};
return data;
}
function _eventKit() {
const data = require("event-kit");
_eventKit = function () {
return data;
};
return data;
}
function _compression() {
const data = require("./compression");
_compression = function () {
return data;
};
return data;
}
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* strict
* @format
*/
const logger = (0, _log4js().getLogger)('reliable-socket'); // Do not synchronously compress large payloads (risks blocking the event loop)
var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js');var _log4js;
function _load_log4js() {return _log4js = require('log4js');}var _eventKit;
function _load_eventKit() {return _eventKit = require('event-kit');}var _compression;
function _load_compression() {return _compression = require('./compression');} /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/const logger = (0, (_log4js || _load_log4js()).getLogger)('nuclide-server'); // Do not synchronously compress large payloads (risks blocking the event loop)
const MAX_SYNC_COMPRESS_LENGTH = 100000;
// An unreliable transport for sending JSON formatted messages

@@ -42,69 +63,38 @@ // over a WebSocket

class WebSocketTransport {
constructor(clientId, socket, options) {
this.id = clientId;
this._emitter = new (_eventKit || _load_eventKit()).Emitter();
this._emitter = new (_eventKit().Emitter)();
this._socket = socket;
this._messages = new _rxjsBundlesRxMinJs.Subject();
this._syncCompression =
options == null || options.syncCompression !== false;
this._messages = new _RxMin.Subject();
this._syncCompression = options == null || options.syncCompression !== false;
logger.info('Client #%s connecting with a new socket!', this.id);
socket.on('message', data => {
let message = data;
// Only compressed data will be sent as binary buffers.
let message = data; // Only compressed data will be sent as binary buffers.
if (typeof data !== 'string') {
message = (0, (_compression || _load_compression()).decompress)(data);
message = (0, _compression().decompress)(data);
}
this._onSocketMessage(message);
this._messages.next(message);
});
socket.on('close', () => {
if (this._socket != null) {if (!(
this._socket === socket)) {throw new Error('Invariant violation: "this._socket === socket"');}
logger.info(
'Client #%s socket close received on open socket!',
this.id);
if (!(this._socket === socket)) {
throw new Error("Invariant violation: \"this._socket === socket\"");
}
this._setClosed();
} else {
logger.info(
'Client #%s received socket close on already closed socket!',
this.id);
logger.info('Client #%s socket close received on open socket!', this.id);
}
this._setClosed();
});
socket.on('error', e => {
logger.error(`Client #${this.id} error: ${e.message}`);
socket.on('error', e => {
if (this._socket != null) {
logger.error(`Client #${this.id} error: ${e.message}`);
this._emitter.emit('error', e);
} else {
logger.error(`Client #${this.id} error after close: ${e.message}`);
}
this._emitter.emit('error', e);
});
socket.on('pong', data => {
if (this._socket != null) {
// data may be a Uint8Array
this._emitter.emit('pong', data != null ? String(data) : data);
} else {
logger.warn('Received socket pong after connection closed');
}
// data may be a Uint8Array
this._emitter.emit('pong', data != null ? String(data) : data);
});
}
_onSocketMessage(message) {
if (this._socket == null) {
logger.warn('Received socket message after connection closed');
return;
}
this._messages.next(message);
}
onMessage() {

@@ -124,7 +114,5 @@ return this._messages;

const socket = this._socket;
if (socket == null) {
logger.error(
'Attempt to send socket message after connection closed',
new Error());
logger.error('Attempt to send socket message after connection closed', new Error());
return Promise.resolve(false);

@@ -136,7 +124,11 @@ }

let compressed = false;
if (this._syncCompression && message.length < MAX_SYNC_COMPRESS_LENGTH) {
data = (0, (_compression || _load_compression()).compress)(message);
data = (0, _compression().compress)(message);
compressed = true;
}
socket.send(data, { compress: !compressed }, err => {
socket.send(data, {
compress: !compressed
}, err => {
if (err != null) {

@@ -150,8 +142,12 @@ logger.warn(`Failed sending to client:${this.id} message:${message}`);

});
}
} // The WS socket automatically responds to pings with pongs.
// The WS socket automatically responds to pings with pongs.
ping(data) {
if (this._socket != null) {
this._socket.ping(data);
try {
this._socket.ping(data);
} catch (e) {
logger.error('Attempted to ping on the socket and got an error:', e);
}
} else {

@@ -170,2 +166,3 @@ logger.error('Attempted to send socket ping after connection closed');

this._socket.close();
this._setClosed();

@@ -181,7 +178,14 @@ }

if (this._socket != null) {
// In certain (Error) conditions socket.close may not emit the on close
this._socket.removeAllListeners(); // In certain (Error) conditions socket.close may not emit the on close
// event synchronously.
this._socket = null;
this._emitter.emit('close');
}
}}exports.WebSocketTransport = WebSocketTransport;
}
}
exports.WebSocketTransport = WebSocketTransport;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc