<commit_before>var bcoin = require('../bcoin');
var hash = require('hash.js');
var bn = require('bn.js');
var inherits = require('inherits');
var EventEmitter = require('events').EventEmitter;
var utils = bcoin.utils;
var assert = utils.assert;
var constants = bcoin.protocol.constants;

function Wallet(options, passphrase) {
  if (!(this instanceof Wallet))
    return new Wallet(options, passphrase);

  EventEmitter.call(this);

  // bcoin.wallet('scope', 'password')
  if (typeof options === 'string' && typeof passphrase === 'string') {
    options = {
      scope: options,
      passphrase: passphrase
    };
  }
  if (!options)
    options = {};

  this.compressed = typeof options.compressed !== 'undefined' ?
      options.compressed : true;
  this.storage = options.storage;
  this.key = null;
  this.loaded = false;
  this.lastTs = 0;
  this.sharedKeys = options.sharedKeys;

  if (options.priv instanceof bcoin.hd.priv) {
    this.hd = options.priv;
    this.key = this.hd.pair;
  } else if (options.pub instanceof bcoin.hd.pub) {
    this.hd = options.pub;
    this.key = this.hd.pair;
  } else if (options.hd) {
    this.hd = bcoin.hd.priv(options);
    this.key = this.hd.pair;
  } else if (options.passphrase) {
    this.key = bcoin.ecdsa.genKeyPair({
      pers: options.scope,
      entropy: hash.sha256().update(options.passphrase).digest()
    });
  } else if (options.priv || options.pub) {
    this.key = bcoin.ecdsa.keyPair({
      priv: options.priv,
      pub: options.pub
    });
  } else {
    this.key = bcoin.ecdsa.genKeyPair();
  }

  this.addressType = options.addressType || 'normal';

  // Multisig
  this.sharedKeys = (options.sharedKeys || []).map(utils.toKeyArray);
  this.m = options.m || 1;
  this.n = options.n || 1;

  // Use p2sh multisig by default
  if (!options.addressType && this.sharedKeys.length) {
    this.addressType = 'p2sh';
  }

  if (this.m < 1 || this.m > this.n) {
    throw new Error('m ranges between 1 and n');
  }

  if (this.n < 1 || this.n > 7) {
    throw new Error('n ranges between 1 and 7');
  }

  if (this.sharedKeys.length !== this.m - 1) {
    throw new Error(this.m + ' public keys required');
  }

  this.prefix = 'bt/' + this.getAddress() + '/';
  this.tx = new bcoin.txPool(this);

  // Just a constants, actually
  this.fee = 10000;
  this.dust = 5460;

  this._init();
}
inherits(Wallet, EventEmitter);
module.exports = Wallet;

Wallet.prototype._init = function init() {
  if (this.tx._loaded) {
    this.loaded = true;
    return;
  }

  // Notify owners about new accepted transactions
  var self = this;
  var prevBalance = null;
  this.tx.on('update', function(lastTs, tx) {
    var b = this.balance();
    if (prevBalance && prevBalance.cmp(b) !== 0)
      self.emit('balance', b);
    self.emit('update', tx);
    prevBalance = b;
  });

  this.tx.on('tx', function(tx) {
    self.emit('tx', tx);
  });

  this.tx.once('load', function(ts) {
    self.loaded = true;
    self.lastTs = ts;
    self.emit('load', ts);
  });

  this.tx.on('error', function(err) {
    self.emit('error', err);
  });
};

Wallet.prototype.getPrivateKey = function getPrivateKey(enc) {
  var priv = this.key.getPrivate();
  if (priv)
    priv = priv.toArray();
  else
    return;
  if (!enc)
    return priv;

  if (enc === 'base58') {
    // We'll be using ncompressed public key as an address
    var arr = [ 128 ];

    // 0-pad key
    while (arr.length + priv.length < 33)
      arr.push(0);
    arr = arr.concat(priv);
    if (this.compressed)
      arr.push(1);
    var chk = utils.checksum(arr);
    return utils.toBase58(arr.concat(chk));
  } else {
    return priv;
  }
};

Wallet.prototype.getPublicKey = function getPublicKey(enc) {
  var pub;
  if (this.addressType === 'p2sh')
    pub = this.getRedemption();
  else
    pub = this.key.getPublic(this.compressed, 'array');

  if (enc === 'base58')
    return utils.toBase58(pub);
  else if (enc === 'hex')
    return utils.toHex(pub);
  else
    return pub;
};

Wallet.prototype.getHash = function getHash() {
  return utils.ripesha(this.getPublicKey());
};

Wallet.prototype.getAddress = function getAddress() {
  return Wallet.hash2addr(this.getHash(), this.addressType);
};

Wallet.hash2addr = function hash2addr(hash, version) {
  hash = utils.toArray(hash, 'hex');

  version = constants.addr[version || 'normal'];
  hash = [ version ].concat(hash);

  var addr = hash.concat(utils.checksum(hash));
  return utils.toBase58(addr);
};

Wallet.addr2hash = function addr2hash(addr, version) {
  if (!Array.isArray(addr))
    addr = utils.fromBase58(addr);

  version = constants.addr[version || 'normal'];

  if (addr.length !== 25)
    return [];
  if (addr[0] !== version)
    return [];

  var chk = utils.checksum(addr.slice(0, -4));
  if (utils.readU32(chk, 0) !== utils.readU32(addr, 21))
    return [];

  return addr.slice(1, -4);
};

Wallet.prototype.validateAddress = function validateAddress(addr, version) {
  var p = Wallet.addr2hash(addr, version);
  return p.length !== 0;
};
Wallet.validateAddress = Wallet.prototype.validateAddress;

Wallet.prototype.ownOutput = function ownOutput(tx, index) {
  var hash = this.getHash();
  var key = this.getPublicKey();

  var outputs = tx.outputs.filter(function(output, i) {
    if (index !== undefined && index !== i)
      return false;

    var s = output.script;

    if (bcoin.script.isPubkeyhash(s, hash))
      return true;

    if (bcoin.script.isSimplePubkeyhash(s, hash))
      return true;

    if (bcoin.script.isMultisig(s, key))
      return true;

    if (bcoin.script.isScripthash(s, hash))
      return true;

    return false;
  }, this);
  if (outputs.length === 0)
    return false;

  return outputs;
};

Wallet.prototype.ownInput = function ownInput(tx, index) {
  var hash = this.getHash();
  var key = this.getPublicKey();

  var inputs = tx.inputs.filter(function(input, i) {
    if (index !== undefined && index !== i)
      return false;

    if (bcoin.script.isPubkeyhashInput(input.script) &&
        utils.isEqual(input.script[1], key))
      return true;

    if (!input.out.tx)
      return false;

    var s = input.out.tx.outputs[input.out.index].script;
    if (bcoin.script.isPubkeyhash(s, hash))
      return true;

    if (bcoin.script.isMultisig(s, key))
      return true;

    if (bcoin.script.isScripthash(s, hash))
      return true;

    return false;
  }, this);
  if (inputs.length === 0)
    return false;

  return inputs;
};

Wallet.prototype.sign = function sign(tx, type, inputs, off) {
  if (!type)
    type = 'all';

  if (!off)
    off = 0;

  var pub = this.getPublicKey();
  inputs = inputs || tx.inputs;

  // Add signature script to each input
  inputs = inputs.filter(function(input, i) {
    // Filter inputs that this wallet own
    if (!input.out.tx || !this.ownOutput(input.out.tx))
      return false;

    // Get the previous output's subscript
    var s = input.out.tx.getSubscript(input.out.index);

    // Get the hash of the current tx, minus the other inputs, plus the sighash.
    // `off` is used here in a case where we have multiple wallet objects
    // signing the same tx.
    var hash = tx.subscriptHash(off + i, s, type);

    // Sign the transaction with our one input
    var signature = bcoin.ecdsa.sign(hash, this.key).toDER();

    // Add the sighash as a single byte to the signature
    signature = signature.concat(bcoin.protocol.constants.hashType[type]);

    // P2PKH and simple tx
    if (bcoin.script.isPubkeyhash(s) || bcoin.script.isSimplePubkeyhash(s)) {
      input.script = [ signature, pub ];
      return true;
    }

    // Multisig
    // empty array == OP_FALSE == OP_0
    // raw format: OP_FALSE [sig-1] [sig-2] ...
    // p2sh format: OP_FALSE [sig-1] [sig-2] ... [redeem-script]
    if (bcoin.script.isMultisig(s) || bcoin.script.isScripthash(s)) {
      if (!input.script || !input.script.length) {
        input.script = [ [], signature ];
      } else if (!~input.script.indexOf(signature)) {
        input.script.push(signature);
      }
    }

    // P2SH requires a redeem script after signatures
    if (bcoin.script.isScripthash(s)) {
      if (input.script.length - 1 === this.m) {
        input.script.push(this.getRedemption());
      }
    }

    return true;
  }, this);

  return inputs.length;
};

Wallet.prototype.sign = function sign(tx, type, inputs) {
  if (!type)
    type = 'all';

  var pub = this.getPublicKey();
  var key = this.key;
  var nsigs = this.m;

  inputs = inputs || tx.inputs;

  // Add signature script to each input
  inputs = inputs.filter(function(input) {
    // Filter inputs that this wallet own
    if (!input.out.tx || !this.ownOutput(input.out.tx))
      return false;

    tx.scriptSig(input, key, pub, nsigs);

    return true;
  }, this);

  return inputs.length;
};

Wallet.prototype.addTX = function addTX(tx, block) {
  return this.tx.add(tx);
};

Wallet.prototype.all = function all() {
  return this.tx.all();
};

Wallet.prototype.unspent = function unspent() {
  return this.tx.unspent();
};

Wallet.prototype.pending = function pending() {
  return this.tx.pending();
};

Wallet.prototype.balance = function balance() {
  return this.tx.balance();
};

Wallet.prototype.fill = function fill(tx, options, cb) {
  if ((cb && typeof cb === 'object') || options == null) {
    cb = options;
    options = {};
  }
  cb = utils.asyncify(cb);

  if (options._getChange) {
    tx = tx.clone();
  }

  // NOTE: tx should be prefilled with all outputs
  var cost = tx.funds('out');

  // Use initial fee for starters
  var fee = 1;

  // total = cost + fee
  var total = cost.add(new bn(this.fee));

  var lastAdded = 0;
  function addInput(unspent, i) {
    // Add new inputs until TX will have enough funds to cover both
    // minimum post cost and fee
    tx.input(unspent);
    lastAdded++;
    return tx.funds('in').cmp(total) < 0;
  }

  // Transfer `total` funds maximum
  var unspent = this.unspent();
  unspent.every(addInput, this);

  // Add dummy output (for `left`) to calculate maximum TX size
  tx.out(options.change || this, new bn(0));

  // Change fee value if it is more than 1024 bytes
  // (10000 satoshi for every 1024 bytes)
  do {
    // Calculate maximum possible size after signing
    var byteSize = tx.maxSize();

    var addFee = Math.ceil(byteSize / 1024) - fee;
    total.iadd(new bn(addFee * this.fee));
    fee += addFee;

    // Failed to get enough funds, add more inputs
    if (tx.funds('in').cmp(total) < 0)
      unspent.slice(lastAdded).every(addInput, this);
  } while (tx.funds('in').cmp(total) < 0 && lastAdded < unspent.length);

  // Still failing to get enough funds, notify caller
  if (tx.funds('in').cmp(total) < 0) {
    var err = new Error('Not enough funds');
    err.minBalance = total;
    return cb(err);
  }

  // How much money is left after sending outputs
  var left = tx.funds('in').sub(total);

  if (options._getChange) {
    return left;
  }

  // Not enough money, transfer everything to owner
  if (left.cmpn(this.dust) < 0) {
    // NOTE: that this output is either `postCost` or one of the `dust` values
    tx.outputs[tx.outputs.length - 2].value.iadd(left);
    left = new bn(0);
  }

  // Change or remove last output if there is some money left
  if (left.cmpn(0) === 0)
    tx.outputs.pop();
  else
    tx.outputs[tx.outputs.length - 1].value = left;

  // Sign transaction
  if (options.sign === false) {
    this.signEmpty(tx);
  } else {
    this.sign(tx);
  }

  cb(null, tx);

  return tx;
};

Wallet.prototype.getChange = function fill(tx) {
  return this.fill(tx, { _getChange: true });
};

/**
 * P2SH+Multisig Redemption
 */

Wallet.prototype.getRedemption = function() {
  var sharedKeys = this.sharedKeys.slice().map(utils.toKeyArray);
  if (sharedKeys.length < this.m) {
    var pub = this.key.getPublic(this.compressed, 'array');
    sharedKeys.push(pub);
  }
  return bcoin.script.redemption(sharedKeys, m, n);
};

Wallet.prototype.toJSON = function toJSON() {
  return {
    v: 1,
    type: 'wallet',
    pub: utils.toBase58(this.key.getPublic(this.compressed, 'array')),
    priv: this.getPrivateKey('base58'),
    tx: this.tx.toJSON(),
    addressType: this.addressType,
    sharedKeys: utils.toBase58(this.sharedKeys),
    m: this.m,
    n: this.n
  };
};

Wallet.fromJSON = function fromJSON(json) {
  assert.equal(json.v, 1);
  assert.equal(json.type, 'wallet');

  var priv;
  var pub;
  var compressed;

  if (json.priv) {
    var key = bcoin.utils.fromBase58(json.priv);
    assert(utils.isEqual(key.slice(-4), utils.checksum(key.slice(0, -4))));
    assert.equal(key[0], 128);

    key = key.slice(0, -4);
    if (key.length === 34) {
      assert.equal(key[33], 1);
      priv = key.slice(1, -1);
      compressed = true;
    } else {
      priv = key.slice(1);
      compressed = false;
    }
  } else {
    pub = bcoin.utils.fromBase58(json.pub);
    compressed = pub[0] !== 0x04;
  }

  var w = new Wallet({
    priv: priv,
    pub: pub,
    compressed: compressed,
    addressType: json.addressType,
    sharedKeys: json.sharedKeys,
    m: json.m,
    n: json.n
  });

  w.tx.fromJSON(json.tx);

  return w;
};
<commit_msg>remove noSign and getChange for now.<commit_after>var bcoin = require('../bcoin');
var hash = require('hash.js');
var bn = require('bn.js');
var inherits = require('inherits');
var EventEmitter = require('events').EventEmitter;
var utils = bcoin.utils;
var assert = utils.assert;
var constants = bcoin.protocol.constants;

function Wallet(options, passphrase) {
  if (!(this instanceof Wallet))
    return new Wallet(options, passphrase);

  EventEmitter.call(this);

  // bcoin.wallet('scope', 'password')
  if (typeof options === 'string' && typeof passphrase === 'string') {
    options = {
      scope: options,
      passphrase: passphrase
    };
  }
  if (!options)
    options = {};

  this.compressed = typeof options.compressed !== 'undefined' ?
      options.compressed : true;
  this.storage = options.storage;
  this.key = null;
  this.loaded = false;
  this.lastTs = 0;
  this.sharedKeys = options.sharedKeys;

  if (options.priv instanceof bcoin.hd.priv) {
    this.hd = options.priv;
    this.key = this.hd.pair;
  } else if (options.pub instanceof bcoin.hd.pub) {
    this.hd = options.pub;
    this.key = this.hd.pair;
  } else if (options.hd) {
    this.hd = bcoin.hd.priv(options);
    this.key = this.hd.pair;
  } else if (options.passphrase) {
    this.key = bcoin.ecdsa.genKeyPair({
      pers: options.scope,
      entropy: hash.sha256().update(options.passphrase).digest()
    });
  } else if (options.priv || options.pub) {
    this.key = bcoin.ecdsa.keyPair({
      priv: options.priv,
      pub: options.pub
    });
  } else {
    this.key = bcoin.ecdsa.genKeyPair();
  }

  this.addressType = options.addressType || 'normal';

  // Multisig
  this.sharedKeys = (options.sharedKeys || []).map(utils.toKeyArray);
  this.m = options.m || 1;
  this.n = options.n || 1;

  // Use p2sh multisig by default
  if (!options.addressType && this.sharedKeys.length) {
    this.addressType = 'p2sh';
  }

  if (this.m < 1 || this.m > this.n) {
    throw new Error('m ranges between 1 and n');
  }

  if (this.n < 1 || this.n > 7) {
    throw new Error('n ranges between 1 and 7');
  }

  if (this.sharedKeys.length !== this.m - 1) {
    throw new Error(this.m + ' public keys required');
  }

  this.prefix = 'bt/' + this.getAddress() + '/';
  this.tx = new bcoin.txPool(this);

  // Just a constants, actually
  this.fee = 10000;
  this.dust = 5460;

  this._init();
}
inherits(Wallet, EventEmitter);
module.exports = Wallet;

Wallet.prototype._init = function init() {
  if (this.tx._loaded) {
    this.loaded = true;
    return;
  }

  // Notify owners about new accepted transactions
  var self = this;
  var prevBalance = null;
  this.tx.on('update', function(lastTs, tx) {
    var b = this.balance();
    if (prevBalance && prevBalance.cmp(b) !== 0)
      self.emit('balance', b);
    self.emit('update', tx);
    prevBalance = b;
  });

  this.tx.on('tx', function(tx) {
    self.emit('tx', tx);
  });

  this.tx.once('load', function(ts) {
    self.loaded = true;
    self.lastTs = ts;
    self.emit('load', ts);
  });

  this.tx.on('error', function(err) {
    self.emit('error', err);
  });
};

Wallet.prototype.getPrivateKey = function getPrivateKey(enc) {
  var priv = this.key.getPrivate();
  if (priv)
    priv = priv.toArray();
  else
    return;
  if (!enc)
    return priv;

  if (enc === 'base58') {
    // We'll be using ncompressed public key as an address
    var arr = [ 128 ];

    // 0-pad key
    while (arr.length + priv.length < 33)
      arr.push(0);
    arr = arr.concat(priv);
    if (this.compressed)
      arr.push(1);
    var chk = utils.checksum(arr);
    return utils.toBase58(arr.concat(chk));
  } else {
    return priv;
  }
};

Wallet.prototype.getPublicKey = function getPublicKey(enc) {
  var pub;
  if (this.addressType === 'p2sh')
    pub = this.getRedemption();
  else
    pub = this.key.getPublic(this.compressed, 'array');

  if (enc === 'base58')
    return utils.toBase58(pub);
  else if (enc === 'hex')
    return utils.toHex(pub);
  else
    return pub;
};

Wallet.prototype.getHash = function getHash() {
  return utils.ripesha(this.getPublicKey());
};

Wallet.prototype.getAddress = function getAddress() {
  return Wallet.hash2addr(this.getHash(), this.addressType);
};

Wallet.hash2addr = function hash2addr(hash, version) {
  hash = utils.toArray(hash, 'hex');

  version = constants.addr[version || 'normal'];
  hash = [ version ].concat(hash);

  var addr = hash.concat(utils.checksum(hash));
  return utils.toBase58(addr);
};

Wallet.addr2hash = function addr2hash(addr, version) {
  if (!Array.isArray(addr))
    addr = utils.fromBase58(addr);

  version = constants.addr[version || 'normal'];

  if (addr.length !== 25)
    return [];
  if (addr[0] !== version)
    return [];

  var chk = utils.checksum(addr.slice(0, -4));
  if (utils.readU32(chk, 0) !== utils.readU32(addr, 21))
    return [];

  return addr.slice(1, -4);
};

Wallet.prototype.validateAddress = function validateAddress(addr, version) {
  var p = Wallet.addr2hash(addr, version);
  return p.length !== 0;
};
Wallet.validateAddress = Wallet.prototype.validateAddress;

Wallet.prototype.ownOutput = function ownOutput(tx, index) {
  var hash = this.getHash();
  var key = this.getPublicKey();

  var outputs = tx.outputs.filter(function(output, i) {
    if (index !== undefined && index !== i)
      return false;

    var s = output.script;

    if (bcoin.script.isPubkeyhash(s, hash))
      return true;

    if (bcoin.script.isSimplePubkeyhash(s, hash))
      return true;

    if (bcoin.script.isMultisig(s, key))
      return true;

    if (bcoin.script.isScripthash(s, hash))
      return true;

    return false;
  }, this);
  if (outputs.length === 0)
    return false;

  return outputs;
};

Wallet.prototype.ownInput = function ownInput(tx, index) {
  var hash = this.getHash();
  var key = this.getPublicKey();

  var inputs = tx.inputs.filter(function(input, i) {
    if (index !== undefined && index !== i)
      return false;

    if (bcoin.script.isPubkeyhashInput(input.script) &&
        utils.isEqual(input.script[1], key))
      return true;

    if (!input.out.tx)
      return false;

    var s = input.out.tx.outputs[input.out.index].script;
    if (bcoin.script.isPubkeyhash(s, hash))
      return true;

    if (bcoin.script.isMultisig(s, key))
      return true;

    if (bcoin.script.isScripthash(s, hash))
      return true;

    return false;
  }, this);
  if (inputs.length === 0)
    return false;

  return inputs;
};

Wallet.prototype.sign = function sign(tx, type, inputs, off) {
  if (!type)
    type = 'all';

  if (!off)
    off = 0;

  var pub = this.getPublicKey();
  inputs = inputs || tx.inputs;

  // Add signature script to each input
  inputs = inputs.filter(function(input, i) {
    // Filter inputs that this wallet own
    if (!input.out.tx || !this.ownOutput(input.out.tx))
      return false;

    // Get the previous output's subscript
    var s = input.out.tx.getSubscript(input.out.index);

    // Get the hash of the current tx, minus the other inputs, plus the sighash.
    // `off` is used here in a case where we have multiple wallet objects
    // signing the same tx.
    var hash = tx.subscriptHash(off + i, s, type);

    // Sign the transaction with our one input
    var signature = bcoin.ecdsa.sign(hash, this.key).toDER();

    // Add the sighash as a single byte to the signature
    signature = signature.concat(bcoin.protocol.constants.hashType[type]);

    // P2PKH and simple tx
    if (bcoin.script.isPubkeyhash(s) || bcoin.script.isSimplePubkeyhash(s)) {
      input.script = [ signature, pub ];
      return true;
    }

    // Multisig
    // empty array == OP_FALSE == OP_0
    // raw format: OP_FALSE [sig-1] [sig-2] ...
    // p2sh format: OP_FALSE [sig-1] [sig-2] ... [redeem-script]
    if (bcoin.script.isMultisig(s) || bcoin.script.isScripthash(s)) {
      if (!input.script || !input.script.length) {
        input.script = [ [], signature ];
      } else if (!~input.script.indexOf(signature)) {
        input.script.push(signature);
      }
    }

    // P2SH requires a redeem script after signatures
    if (bcoin.script.isScripthash(s)) {
      if (input.script.length - 1 === this.m) {
        input.script.push(this.getRedemption());
      }
    }

    return true;
  }, this);

  return inputs.length;
};

Wallet.prototype.sign = function sign(tx, type, inputs) {
  if (!type)
    type = 'all';

  var pub = this.getPublicKey();
  var key = this.key;
  var nsigs = this.m;

  inputs = inputs || tx.inputs;

  // Add signature script to each input
  inputs = inputs.filter(function(input) {
    // Filter inputs that this wallet own
    if (!input.out.tx || !this.ownOutput(input.out.tx))
      return false;

    tx.scriptSig(input, key, pub, nsigs);

    return true;
  }, this);

  return inputs.length;
};

Wallet.prototype.addTX = function addTX(tx, block) {
  return this.tx.add(tx);
};

Wallet.prototype.all = function all() {
  return this.tx.all();
};

Wallet.prototype.unspent = function unspent() {
  return this.tx.unspent();
};

Wallet.prototype.pending = function pending() {
  return this.tx.pending();
};

Wallet.prototype.balance = function balance() {
  return this.tx.balance();
};

Wallet.prototype.fill = function fill(tx, cb) {
  cb = utils.asyncify(cb);

  // NOTE: tx should be prefilled with all outputs
  var cost = tx.funds('out');

  // Use initial fee for starters
  var fee = 1;

  // total = cost + fee
  var total = cost.add(new bn(this.fee));

  var lastAdded = 0;
  function addInput(unspent, i) {
    // Add new inputs until TX will have enough funds to cover both
    // minimum post cost and fee
    tx.input(unspent);
    lastAdded++;
    return tx.funds('in').cmp(total) < 0;
  }

  // Transfer `total` funds maximum
  var unspent = this.unspent();
  unspent.every(addInput, this);

  // Add dummy output (for `left`) to calculate maximum TX size
  tx.out(options.change || this, new bn(0));

  // Change fee value if it is more than 1024 bytes
  // (10000 satoshi for every 1024 bytes)
  do {
    // Calculate maximum possible size after signing
    var byteSize = tx.maxSize();

    var addFee = Math.ceil(byteSize / 1024) - fee;
    total.iadd(new bn(addFee * this.fee));
    fee += addFee;

    // Failed to get enough funds, add more inputs
    if (tx.funds('in').cmp(total) < 0)
      unspent.slice(lastAdded).every(addInput, this);
  } while (tx.funds('in').cmp(total) < 0 && lastAdded < unspent.length);

  // Still failing to get enough funds, notify caller
  if (tx.funds('in').cmp(total) < 0) {
    var err = new Error('Not enough funds');
    err.minBalance = total;
    return cb(err);
  }

  // How much money is left after sending outputs
  var left = tx.funds('in').sub(total);

  // Not enough money, transfer everything to owner
  if (left.cmpn(this.dust) < 0) {
    // NOTE: that this output is either `postCost` or one of the `dust` values
    tx.outputs[tx.outputs.length - 2].value.iadd(left);
    left = new bn(0);
  }

  // Change or remove last output if there is some money left
  if (left.cmpn(0) === 0)
    tx.outputs.pop();
  else
    tx.outputs[tx.outputs.length - 1].value = left;

  // Sign transaction
  // XXX Do not necessarily call this here
  this.sign(tx);

  cb(null, tx);

  return tx;
};

/**
 * P2SH+Multisig Redemption
 */

Wallet.prototype.getRedemption = function() {
  var sharedKeys = this.sharedKeys.slice().map(utils.toKeyArray);
  if (sharedKeys.length < this.m) {
    var pub = this.key.getPublic(this.compressed, 'array');
    sharedKeys.push(pub);
  }
  return bcoin.script.redemption(sharedKeys, m, n);
};

Wallet.prototype.toJSON = function toJSON() {
  return {
    v: 1,
    type: 'wallet',
    pub: utils.toBase58(this.key.getPublic(this.compressed, 'array')),
    priv: this.getPrivateKey('base58'),
    tx: this.tx.toJSON(),
    addressType: this.addressType,
    sharedKeys: utils.toBase58(this.sharedKeys),
    m: this.m,
    n: this.n
  };
};

Wallet.fromJSON = function fromJSON(json) {
  assert.equal(json.v, 1);
  assert.equal(json.type, 'wallet');

  var priv;
  var pub;
  var compressed;

  if (json.priv) {
    var key = bcoin.utils.fromBase58(json.priv);
    assert(utils.isEqual(key.slice(-4), utils.checksum(key.slice(0, -4))));
    assert.equal(key[0], 128);

    key = key.slice(0, -4);
    if (key.length === 34) {
      assert.equal(key[33], 1);
      priv = key.slice(1, -1);
      compressed = true;
    } else {
      priv = key.slice(1);
      compressed = false;
    }
  } else {
    pub = bcoin.utils.fromBase58(json.pub);
    compressed = pub[0] !== 0x04;
  }

  var w = new Wallet({
    priv: priv,
    pub: pub,
    compressed: compressed,
    addressType: json.addressType,
    sharedKeys: json.sharedKeys,
    m: json.m,
    n: json.n
  });

  w.tx.fromJSON(json.tx);

  return w;
};
<|endoftext|>


<commit_before>/**
 * Browserevent
 */

var fs = require('fs'),
    http = require('http'),
    path = require('path'),
    socketio = require('socket.io'),
    addEventListener = require('./addEventListener'),
    removeEventListener = require('./removeEventListener');

var BrowserEvent = module.exports.init = function(webdriverInstance) {

    var that = this;

    if (!webdriverInstance) {
        throw new Error('A WebdriverIO instance is needed to initialise browserevent');
    }

    this.browserEvents = {};

    /**
     * initialize socket connection
     */
    this.app = http.createServer();
    this.io  = socketio.listen(this.app);
    this.app.listen(4321, '0.0.0.0');

    this.io.sockets.on('connection', function (socket) {
        that.socket = socket;
    });

    /**
     * read crx extension and add it as desiredCapability
     */
    this.crx = fs.readFileSync(path.join(__dirname, '..', 'extensions/chrome.crx')).toString('base64');

    if (!webdriverInstance.desiredCapabilities.chromeOptions) {
        webdriverInstance.desiredCapabilities.chromeOptions = {};
    }
    if (!webdriverInstance.desiredCapabilities.chromeOptions.extensions || Object.prototype.toString.call(webdriverInstance.desiredCapabilities.chromeOptions.extensions) !== '[object Array]') {
        webdriverInstance.desiredCapabilities.chromeOptions.extensions = [];
    }
    webdriverInstance.desiredCapabilities.chromeOptions.extensions.push(this.crx);

    /**
     * enhance webdriverio eventhandler
     * this needs to be done with the trick that we've already used in the WebdriverIO EventHandler
     * see https://github.com/webdriverio/webdriverio/blob/master/lib/utils/EventHandler.js#L32 for
     * more informations
     *
     * but this time we need to tweak the trick a bit to ensure that the socket request gets send
     * after a socket connection was established
     */
    webdriverInstance.addEventListener = function() {

        var args = arguments;

        webdriverInstance.addCommand('__addEventListener', function(done) {

            /**
             * if socket connection was already established go ahead and
             * register event
             */
            if(that.socket) {
                addEventListener.apply(that, Array.prototype.slice.call(args));
                return done();
            }

            /**
             * if not, wait until we receive a vital sign form the client browser
             */
            that.io.sockets.on('connection', function (socket) {
                that.socket = socket;
                addEventListener.apply(that, Array.prototype.slice.call(args));
                done();
            });

        });

        /**
         * execute custom command immediately
         */
        webdriverInstance.__addEventListener();

        /**
         * remove custom command to avoid a possible double execution
         */
        webdriverInstance.__addEventListener = undefined;

        /**
         * return "this" to make commands chainable
         */
        return webdriverInstance;

    };

    /**
     * register removeEventListener the traditional way
     */
    webdriverInstance.eventHandler.removeEventListener = removeEventListener(this);
    webdriverInstance.eventHandler.registerCommands('removeEventListener');

    return this;

};<commit_msg>throw error if connection didn't get established after 30s<commit_after>/**
 * Browserevent
 */

var fs = require('fs'),
    http = require('http'),
    path = require('path'),
    socketio = require('socket.io'),
    addEventListener = require('./addEventListener'),
    removeEventListener = require('./removeEventListener');

var BrowserEvent = module.exports.init = function(webdriverInstance) {

    var that = this;

    if (!webdriverInstance) {
        throw new Error('A WebdriverIO instance is needed to initialise browserevent');
    }

    this.browserEvents = {};

    /**
     * initialize socket connection
     */
    this.app = http.createServer();
    this.io  = socketio.listen(this.app);
    this.app.listen(4321, '0.0.0.0');

    this.io.sockets.on('connection', function (socket) {
        that.socket = socket;
    });

    /**
     * read crx extension and add it as desiredCapability
     */
    this.crx = fs.readFileSync(path.join(__dirname, '..', 'extensions/chrome.crx')).toString('base64');

    if (!webdriverInstance.desiredCapabilities.chromeOptions) {
        webdriverInstance.desiredCapabilities.chromeOptions = {};
    }
    if (!webdriverInstance.desiredCapabilities.chromeOptions.extensions || Object.prototype.toString.call(webdriverInstance.desiredCapabilities.chromeOptions.extensions) !== '[object Array]') {
        webdriverInstance.desiredCapabilities.chromeOptions.extensions = [];
    }
    webdriverInstance.desiredCapabilities.chromeOptions.extensions.push(this.crx);

    /**
     * enhance webdriverio eventhandler
     * this needs to be done with the trick that we've already used in the WebdriverIO EventHandler
     * see https://github.com/webdriverio/webdriverio/blob/master/lib/utils/EventHandler.js#L32 for
     * more informations
     *
     * but this time we need to tweak the trick a bit to ensure that the socket request gets send
     * after a socket connection was established
     */
    webdriverInstance.addEventListener = function() {

        var args = arguments,
            isConnected = false;

        webdriverInstance.addCommand('__addEventListener', function(done) {

            /**
             * if socket connection was already established go ahead and
             * register event
             */
            if(that.socket) {
                addEventListener.apply(that, Array.prototype.slice.call(args));
                return done();
            }

            /**
             * if not, wait until we receive a vital sign form the client browser
             */
            that.io.sockets.on('connection', function (socket) {
                that.socket = socket;
                addEventListener.apply(that, Array.prototype.slice.call(args));
                isConnected = true;
                done();
            });

            setTimeout(function() {
                if(isConnected) {
                    return;
                }

                throw new Error('can\'t establish socket connection')
            }, 30000)

        });

        /**
         * execute custom command immediately
         */
        webdriverInstance.__addEventListener();

        /**
         * remove custom command to avoid a possible double execution
         */
        webdriverInstance.__addEventListener = undefined;

        /**
         * return "this" to make commands chainable
         */
        return webdriverInstance;

    };

    /**
     * register removeEventListener the traditional way
     */
    webdriverInstance.eventHandler.removeEventListener = removeEventListener(this);
    webdriverInstance.eventHandler.registerCommands('removeEventListener');

    return this;

};<|endoftext|>


<commit_before>'use strict';

const browserify = require('browserify');
const envify = require('envify/custom');
const uglify = require('uglify-js');
const zlib = require('zlib');

module.exports = (packages, env) => {
  const files = Array.isArray(packages) ? packages : [packages];
  let data = '';

  return new Promise((resolve, reject) => {
    const bundler = browserify();

    files.forEach((file) => {
      bundler.require(file);
    });

    if (env) {
      process.env.NODE_ENV = env;
      bundler.transform(envify({
        NODE_ENV: env
      }));
    }

    bundler.bundle()
      .on('data', (buf) => {
        data += buf.toString();
      })
      .on('end', () => {
        const minify = uglify.minify(data, {
          fromString: true,
          mangle: false,
          compress: {
            dead_code: true
          }
        });
        const results = {
          packages: files,
          bundle: data.length,
          min: minify.code.length
        };
        if (env) {
          results.env = env;
        }
        zlib.gzip(minify.code, (err, buffer) => {
          if (err) {
            console.error(err);
          }
          results.gzip = buffer.toString('base64').length;
          resolve(results);
        });
      });
  });
};
<commit_msg>feat(bundle-bytes): show package version<commit_after>'use strict';

const browserify = require('browserify');
const envify = require('envify/custom');
const uglify = require('uglify-js');
const zlib = require('zlib');

module.exports = (packages, env) => {
  const files = Array.isArray(packages) ? packages : [packages];
  let data = '';
  let pkgIds = files.slice();

  return new Promise((resolve, reject) => {
    const bundler = browserify();

    files.forEach((file, i) => {
      bundler.require(file);
      bundler.on('package', function (pkg) {
        if (pkg.name === file) {
          pkgIds[i] = pkg._id;
        }
      });
    });

    if (env) {
      process.env.NODE_ENV = env;
      bundler.transform(envify({
        NODE_ENV: env
      }));
    }

    bundler.bundle()
      .on('data', (buf) => {
        data += buf.toString();
      })
      .on('end', () => {
        const minify = uglify.minify(data, {
          fromString: true,
          mangle: false,
          compress: {
            dead_code: true
          }
        });
        const results = {
          packages: pkgIds,
          bundle: data.length,
          min: minify.code.length
        };
        if (env) {
          results.env = env;
        }
        zlib.gzip(minify.code, (err, buffer) => {
          if (err) {
            console.error(err);
          }
          results.gzip = buffer.toString('base64').length;
          resolve(results);
        });
      });
  });
};
<|endoftext|>


<commit_before>var util = require('util'),
request  = require('request'),
extend   = require('extend.js');

function BunyanSlack(options) {
	options = options || {};
	if (!options.webhook_url && !options.webhookUrl) {
		throw new Error("webhook url cannot be null");
	} else {

		this.webhook_url = options.webhook_url || options.webhookUrl;
		this.customFormatter = options.customFormatter;
		this.icon_url = options.icon_url || options.iconUrl;
		this.icon_emoji = options.icon_emoji || options.iconEmoji;
		this.channel    = options.channel    || "#general";
		this.username   = options.username   || "Bunyan Slack";

		if (!this.icon_url && !this.icon_emoji) {
			this.icon_emoji = ':scream_cat:';
		}

	}
}

var nameFromLevel = {
	10 : 'trace',
	20 : 'debug',
	30 : 'info',
	40 : 'warn',
	50 : 'error',
	60 : 'fatal'
};

BunyanSlack.prototype.write = function write(record) {

	if (typeof record === "string"){
		record = JSON.parse(record);
	}
	var levelName = nameFromLevel[record.level];

	var message = this.customFormatter ? this.customFormatter(record, levelName) : {
		text: util.format("[%s] %s", levelName.toUpperCase(), record.msg)
	};

	var base = {
		channel: this.channel,
		username: this.username,
		icon_url: this.icon_url,
		icon_emoji: this.icon_emoji
	};

	message = extend(base, message);

	request.post({
		url: this.webhook_url,
		body: JSON.stringify(message)
	});
};

module.exports = BunyanSlack;
<commit_msg>Added an error event handler to the post request to prevent thrown errors from crashing the program.  One example where this would happen is when the network is temporarily disconnected.<commit_after>var util = require('util'),
request  = require('request'),
extend   = require('extend.js');

function BunyanSlack(options) {
	options = options || {};
	if (!options.webhook_url && !options.webhookUrl) {
		throw new Error("webhook url cannot be null");
	} else {

		this.webhook_url = options.webhook_url || options.webhookUrl;
		this.customFormatter = options.customFormatter;
		this.icon_url = options.icon_url || options.iconUrl;
		this.icon_emoji = options.icon_emoji || options.iconEmoji;
		this.channel    = options.channel    || "#general";
		this.username   = options.username   || "Bunyan Slack";

		if (!this.icon_url && !this.icon_emoji) {
			this.icon_emoji = ':scream_cat:';
		}

	}
}

var nameFromLevel = {
	10 : 'trace',
	20 : 'debug',
	30 : 'info',
	40 : 'warn',
	50 : 'error',
	60 : 'fatal'
};

BunyanSlack.prototype.write = function write(record) {

	if (typeof record === "string"){
		record = JSON.parse(record);
	}
	var levelName = nameFromLevel[record.level];

	var message = this.customFormatter ? this.customFormatter(record, levelName) : {
		text: util.format("[%s] %s", levelName.toUpperCase(), record.msg)
	};

	var base = {
		channel: this.channel,
		username: this.username,
		icon_url: this.icon_url,
		icon_emoji: this.icon_emoji
	};

	message = extend(base, message);

	request.post({
		url: this.webhook_url,
		body: JSON.stringify(message)
	}).on('error', function(err) {
        // Error handler to prevent thrown errors from crashing the program.
    });
};

module.exports = BunyanSlack;
<|endoftext|>


<commit_before>var gulp = require('gulp');
var path = require('path');

var gutil = require('gulp-util');

var webpack = require('webpack');
var webpackStream = require('webpack-stream');
var HtmlWebpackPlugin = require('html-webpack-plugin');

var browserSync = require('./server').browserSync;

var config = require('./toolkit.config.js');

var { unixifyPath } = require('./utils');
var { getTypeOfFile, encode } = require('./encodeTask');

var { convertDataSchema, filterDataSchema } = require('./resources/dataschema');
var { filterHyperties, convertHyperty } = require('./resources/hyperty');
var { filterProtostubs, convertProtoStub } = require('./resources/protostub');
var createDescriptor = require('./descriptors').createDescriptor;
var getTypeOfDescriptor = require('./descriptors').getTypeOfDescriptor;

function watchHTML() {

  gulp.watch(['./../server/*.*', process.env.HYPERTY_REPO + '/examples/main.js'], bundleHTML);

  return bundleHTML();
}

function bundleHTML() {

  var webpackConfig = {
    entry: {
      rethink: process.cwd() + '/server/rethink.js'
    },
    output: {
      path: process.cwd() + '/app',
      filename: '[name].js'
    },
    devtool: 'source-map',
    resolve: {
      modules: [
        'node_modules',
        path.resolve(process.env.HYPERTY_REPO + '/' + process.env.HYPERTY_TEMPLATES_PATH)
      ],
      alias: {
        app: path.resolve(process.env.HYPERTY_REPO + '/' + process.env.HYPERTY_TEMPLATES_PATH + '/main.js')
      }
    },
    plugins: [new HtmlWebpackPlugin({
      title: 'Hyperty Toolkit',
      template: 'server/index.ejs',
      files: {
        css: [],
        js: []
      }
    })]
  };

  return gulp.src(['./../server/*.*', process.env.HYPERTY_REPO + '/examples/main.js'])
    .pipe(webpackStream(webpackConfig, webpack))
    .pipe(gulp.dest('app/'));
}

function watchResources(done) {

  gulp.watch(['./resources/*.js'], function(event) {

    var type = getTypeOfFile(event.path);

    switch (type) {
      case 'Runtime':
        var transpileOpts = {};
        transpileOpts.isES6 = false;
        encode(event.path, {}, transpileOpts);
        break;
    }

  });

}

function watchHyperties(done) {

  var sourceCode = getListOfResources('hyperty');
  sourceCode = sourceCode.map(item => item + '/*.js');

  gulp.watch(sourceCode, function(event) {
    console.log('event:', event);
    return watchResource(event, 'hyperty');
  });

  var descriptor = getListOfResources('hyperty');
  descriptor = descriptor.map(item => item + '/*.hy.json');

  gulp.watch(descriptor, function(event) {
    return generateDescriptor('hyperty', event.path);
  });

}

function watchProtostubs(done) {

  var sourceCode = getListOfResources('protocolstub');
  sourceCode = sourceCode.map(item => item + '/*.js');

  console.log('Watch SourceCode:', sourceCode);

  gulp.watch(sourceCode, function(event) {
    return watchResource(event, 'protocolstub');
  });

  var descriptor = getListOfResources('protocolstub');
  descriptor = descriptor.map(item => item + '/*.ps.json');
  gulp.watch(descriptor, function(event) {
    return generateDescriptor('protocolstub', event.path);
  });

}

function watchIdpProxies(done) {

  var sourceCode = getListOfResources('idp-proxy');
  sourceCode = sourceCode.map(item => item + '/*.idp.js');

  // console.log('Watch SourceCode:', sourceCode);

  gulp.watch(sourceCode, function(event) {
    return watchResource(event, 'idp-proxy');
  });

  var descriptor = getListOfResources('idp-proxy');
  descriptor = descriptor.map(item => item + '/*.idp.json');
  gulp.watch(descriptor, function(event) {
    return generateDescriptor('idp-proxy', event.path);
  });

}

function watchDataSchemas(done) {
  var sourceCode = getListOfResources('dataschema');
  sourceCode = sourceCode.map(item => item + '/*.ds.js');

  // Watch the local resources for DataSchemas
  sourceCode.concat([process.cwd() + '/resources/schemas/**/*.ds.json']);

  gulp.watch(sourceCode, function(event) {
    return watchResource(event, 'dataschema');
  });

  var descriptor = getListOfResources('dataschema');
  descriptor = descriptor.map(item => item + '/*.ds.json');

  // Watch the local resources for DataSchemas
  descriptor = descriptor.concat([process.cwd() + '/resources/schemas/**/*.ds.json']);

  // console.log('Watch dataSchema: ', descriptor);
  gulp.watch(descriptor, function(event) {
    return generateDescriptor('dataschema', event.path);
  });
}

function watchResource(event, type) {

  if ((event.type === 'changed' || event.type === 'added') && !event.path.includes('node_modules')) {

    var fileObject = path.parse(event.path);
    var resource;
    var convertFunction;
    var filePath;

    switch (type) {

      case 'hyperty':
        resource = getTypeOfDescriptor('hyperty');
        convertFunction = convertHyperty;
        filePath = path.join(fileObject.dir, '*' + resource.extension + '.js');
        break;

      case 'protocolstub':
        resource = getTypeOfDescriptor('protocolstub');
        convertFunction = convertProtoStub;
        filePath = path.join(fileObject.dir, '*' + resource.extension + '.js');
        break;

      case 'idp-proxy':
        resource = getTypeOfDescriptor('idp-proxy');
        convertFunction = convertProtoStub;
        filePath = path.join(fileObject.dir, '*' + resource.extension + '.js');
        break;

      case 'dataschema':
        resource = getTypeOfDescriptor('dataschema');
        convertFunction = convertDataSchema;
        filePath = path.join(fileObject.dir, '*' + resource.extension + '.js');
        break;
    }

    return gulp.src(filePath)
      .pipe(convertFunction())
      .resume()
      .on('end', function() {
        browserSync.reload();
      });

  }

}

function getListOfResources(type) {

  var resource = getTypeOfDescriptor(type);
  var list;
  var filtered;
  var resourcePath;
  var resourceSrc;

  switch (resource.type) {
    case 'idp-proxy':
    case 'protocolstub':
      list = config.protostubs.include_stubs || ['**'];
      filtered = filterProtostubs(process.env.ENVIRONMENT);
      resourcePath = config.protostubs.repository;
      resourceSrc = config.protostubs.sourceCode;
      break;

    case 'hyperty':
      list = config.hyperties.include_hyperties || ['**'];
      filtered = filterHyperties(process.env.ENVIRONMENT);
      resourcePath = config.hyperties.repository;
      resourceSrc = config.hyperties.sourceCode;
      break;

    case 'dataschema':
      list = config.hyperties.include_hyperties || ['**'];
      filtered = filterDataSchema(process.env.ENVIRONMENT);
      resourcePath = config.hyperties.repository;
      resourceSrc = config.hyperties.sourceCode;
      break;
  }

  if (list.length === 0) {
    list = filtered.map(resource => unixifyPath(resource.dir).replace('/', ''));
  }

  var resources = list.map((item) => {
    item = item.trim();
    return path.join(resourcePath, resourceSrc, item);
  }).filter((item) => {
    return !!filtered.filter(resource => item.includes(resource.dir)).length;
  });

  return resources;
}

/** Create descriptors and source code for Hyperties */

function createHypertiesSourceCode() {
  return generateSourceCode('hyperty');
}

function createHypertiesDescriptors() {
  return generateDescriptor('hyperty');
}

/** Create descriptors and source code for Protostubs */
function createProtoStubsSourceCode() {
  return generateSourceCode('protocolstub');
}

function createProtoStubsDescriptors() {
  return generateDescriptor('protocolstub');
}

/** Create descriptors and source code for IDP Proxies */
function createIDPProxySourceCode() {
  return generateSourceCode('idp-proxy');
}

function createIDPProxyDescriptors() {
  return generateDescriptor('idp-proxy');
}

/** Create descriptors and source code for IDP Proxies */
function createDataSchemasSourceCode() {
  return generateSourceCode('dataschema');
}

function createDataSchemasDescriptors() {
  return generateDescriptor('dataschema');
}

function generateSourceCode(type) {

  var resource = getTypeOfDescriptor(type);
  var list;
  var filtered;
  var repository;
  var convertFunction;

  // console.log('resource:', resource);

  switch (resource.type) {
    case 'idp-proxy':
    case 'protocolstub':
      list = config.protostubs.include_stubs;
      repository = path.join(config.protostubs.repository, config.protostubs.sourceCode);
      filtered = filterProtostubs(process.env.ENVIRONMENT);
      convertFunction = convertProtoStub;
      break;

    case 'hyperty':
      list = config.hyperties.include_hyperties;
      repository = path.join(config.hyperties.repository, config.hyperties.sourceCode);
      filtered = filterHyperties(process.env.ENVIRONMENT);
      convertFunction = convertHyperty;
      break;

    case 'dataschema':
      list = config.hyperties.include_hyperties;
      repository = path.join(config.hyperties.repository, config.hyperties.sourceCode);
      filtered = filterDataSchema(process.env.ENVIRONMENT);
      convertFunction = convertDataSchema;
      break;
  }

  if (list.length === 0) {
    list = filtered.map(resource => unixifyPath(resource.dir).replace('/', ''));
  }

  var sourceCode = list.map((item) => {
    item = item.trim();
    return path.join(repository, item, '*' + resource.extension + '.js');
  }).filter((item) => {
    return !!filtered.filter(resource => item.includes(resource.dir)).length;
  });

  return gulp.src(sourceCode)
    .pipe(convertFunction())
    .on('end', function() {
      browserSync.reload();
    });

}

function generateDescriptor(type, filePath) {

  var resource = getTypeOfDescriptor(type);
  var list;
  var filtered;
  var repository;

  switch (resource.type) {
    case 'idp-proxy':
    case 'protocolstub':
      list = config.protostubs.include_stubs;
      repository = path.join(config.protostubs.repository, config.protostubs.sourceCode);
      filtered = filterProtostubs(process.env.ENVIRONMENT);
      break;

    case 'hyperty':
      list = config.hyperties.include_hyperties;
      repository = path.join(config.hyperties.repository, config.hyperties.sourceCode);
      filtered = filterHyperties(process.env.ENVIRONMENT);
      break;

    case 'dataschema':
      list = config.hyperties.include_hyperties;
      repository = path.join(config.hyperties.repository, config.hyperties.sourceCode);
      filtered = filterDataSchema(process.env.ENVIRONMENT);

  }

  if (list.length === 0) {
    list = filtered.map(resource => unixifyPath(resource.dir).replace('/', ''));
  }

  var descriptor = list.map((item) => {
    item = item.trim();
    return path.join(repository, item, '*' + resource.extension + '.json');
  }).filter((item) => {
    return !!filtered.filter(resource => item.includes(resource.dir)).length;
  });

  if (filePath) { descriptor = filePath; }

  if (resource.type === 'dataschema') {
    descriptor = descriptor.concat('./resources/schemas/**/*.ds.json');
    return gulp.src(descriptor)
      .pipe(convertDataSchema())
      .on('end', function() {
        gutil.log('the pre configuration of ' + resource.type + '  was changed, and the ' + resource.name + '.json was updated');
        browserSync.reload();
      });
  }

  return gulp.src(descriptor)
    .pipe(createDescriptor(resource.type))
    .pipe(gulp.dest('./resources/descriptors/' + resource.name + '.json'))
    .on('end', function() {
      gutil.log('the pre configuration of ' + resource.type + '  was changed, and the ' + resource.name + '.json was updated');
      browserSync.reload();
    });

}

module.exports = {
  createHypertiesSourceCode: createHypertiesSourceCode,
  createHypertiesDescriptors: createHypertiesDescriptors,

  createProtoStubsSourceCode: createProtoStubsSourceCode,
  createProtoStubsDescriptors: createProtoStubsDescriptors,

  createIDPProxySourceCode: createIDPProxySourceCode,
  createIDPProxyDescriptors: createIDPProxyDescriptors,

  createDataSchemasSourceCode: createDataSchemasSourceCode,
  createDataSchemasDescriptors: createDataSchemasDescriptors,

  watchDataSchemas: watchDataSchemas,
  watchIdpProxies: watchIdpProxies,
  watchProtostubs: watchProtostubs,
  watchHyperties: watchHyperties,
  watchResources: watchResources,
  watchHTML: watchHTML
};
<commit_msg>Normalize paths<commit_after>var gulp = require('gulp');
var path = require('path');

var gutil = require('gulp-util');

var webpack = require('webpack');
var webpackStream = require('webpack-stream');
var HtmlWebpackPlugin = require('html-webpack-plugin');

var browserSync = require('./server').browserSync;

var config = require('./toolkit.config.js');

var { unixifyPath } = require('./utils');
var { getTypeOfFile, encode } = require('./encodeTask');

var { convertDataSchema, filterDataSchema } = require('./resources/dataschema');
var { filterHyperties, convertHyperty } = require('./resources/hyperty');
var { filterProtostubs, convertProtoStub } = require('./resources/protostub');
var createDescriptor = require('./descriptors').createDescriptor;
var getTypeOfDescriptor = require('./descriptors').getTypeOfDescriptor;

function watchHTML() {

  gulp.watch(['./../server/*.*', process.env.HYPERTY_REPO + '/examples/main.js'], bundleHTML);

  return bundleHTML();
}

function bundleHTML() {

  var webpackConfig = {
    entry: {
      rethink: process.cwd() + '/server/rethink.js'
    },
    output: {
      path: process.cwd() + '/app',
      filename: '[name].js'
    },
    devtool: 'source-map',
    resolve: {
      modules: [
        'node_modules',
        path.resolve(process.env.HYPERTY_REPO + '/' + process.env.HYPERTY_TEMPLATES_PATH)
      ],
      alias: {
        app: path.resolve(process.env.HYPERTY_REPO + '/' + process.env.HYPERTY_TEMPLATES_PATH + '/main.js')
      }
    },
    plugins: [new HtmlWebpackPlugin({
      title: 'Hyperty Toolkit',
      template: 'server/index.ejs',
      files: {
        css: [],
        js: []
      }
    })]
  };

  return gulp.src(['./../server/*.*', process.env.HYPERTY_REPO + '/examples/main.js'])
    .pipe(webpackStream(webpackConfig, webpack))
    .pipe(gulp.dest('app/'));
}

function watchResources(done) {

  gulp.watch(['./resources/*.js'], function(event) {

    var type = getTypeOfFile(event.path);

    switch (type) {
      case 'Runtime':
        var transpileOpts = {};
        transpileOpts.isES6 = false;
        encode(event.path, {}, transpileOpts);
        break;
    }

  });

}

function watchHyperties(done) {

  var sourceCode = getListOfResources('hyperty');
  sourceCode = sourceCode.map(item => path.join(item, '*.js'));

  gulp.watch(sourceCode, function(event) {
    console.log('event:', event);
    return watchResource(event, 'hyperty');
  });

  var descriptor = getListOfResources('hyperty');
  descriptor = descriptor.map(item => path.join(item, '*.hy.json'));

  gulp.watch(descriptor, function(event) {
    return generateDescriptor('hyperty', event.path);
  });

}

function watchProtostubs(done) {

  var sourceCode = getListOfResources('protocolstub');
  sourceCode = sourceCode.map(item => path.join(item, '*.js'));

  // console.log('Watch SourceCode:', sourceCode);

  gulp.watch(sourceCode, function(event) {
    return watchResource(event, 'protocolstub');
  });

  var descriptor = getListOfResources('protocolstub');
  descriptor = descriptor.map(item => path.join(item, '*.ps.json'));
  gulp.watch(descriptor, function(event) {
    return generateDescriptor('protocolstub', event.path);
  });

}

function watchIdpProxies(done) {

  var sourceCode = getListOfResources('idp-proxy');
  sourceCode = sourceCode.map(item => path.join(item, '*.idp.js'));

  // console.log('Watch SourceCode:', sourceCode);

  gulp.watch(sourceCode, function(event) {
    return watchResource(event, 'idp-proxy');
  });

  var descriptor = getListOfResources('idp-proxy');
  descriptor = descriptor.map(item => path.join(item, '*.idp.json'));
  gulp.watch(descriptor, function(event) {
    return generateDescriptor('idp-proxy', event.path);
  });

}

function watchDataSchemas(done) {
  var sourceCode = getListOfResources('dataschema');
  sourceCode = sourceCode.map(item => path.join(item, '*.ds.js'));

  // Watch the local resources for DataSchemas
  sourceCode.concat([process.cwd() + path.join('resources', 'schemas', '**', '*.ds.json')]);

  gulp.watch(sourceCode, function(event) {
    return watchResource(event, 'dataschema');
  });

  var descriptor = getListOfResources('dataschema');
  descriptor = descriptor.map(item => path.join(item, '*.ds.json'));

  // Watch the local resources for DataSchemas
  descriptor = descriptor.concat([process.cwd() + path.join('resources', 'schemas', '**', '*.ds.json')]);

  // console.log('Watch dataSchema: ', descriptor);
  gulp.watch(descriptor, function(event) {
    return generateDescriptor('dataschema', event.path);
  });
}

function watchResource(event, type) {

  if ((event.type === 'changed' || event.type === 'added') && !event.path.includes('node_modules')) {

    var fileObject = path.parse(event.path);
    var resource;
    var convertFunction;
    var filePath;

    switch (type) {

      case 'hyperty':
        resource = getTypeOfDescriptor('hyperty');
        convertFunction = convertHyperty;
        filePath = path.join(fileObject.dir, '*' + resource.extension + '.js');
        break;

      case 'protocolstub':
        resource = getTypeOfDescriptor('protocolstub');
        convertFunction = convertProtoStub;
        filePath = path.join(fileObject.dir, '*' + resource.extension + '.js');
        break;

      case 'idp-proxy':
        resource = getTypeOfDescriptor('idp-proxy');
        convertFunction = convertProtoStub;
        filePath = path.join(fileObject.dir, '*' + resource.extension + '.js');
        break;

      case 'dataschema':
        resource = getTypeOfDescriptor('dataschema');
        convertFunction = convertDataSchema;
        filePath = path.join(fileObject.dir, '*' + resource.extension + '.js');
        break;
    }

    return gulp.src(filePath)
      .pipe(convertFunction())
      .resume()
      .on('end', function() {
        browserSync.reload();
      });

  }

}

function getListOfResources(type) {

  var resource = getTypeOfDescriptor(type);
  var list;
  var filtered;
  var resourcePath;
  var resourceSrc;

  switch (resource.type) {
    case 'idp-proxy':
    case 'protocolstub':
      list = config.protostubs.include_stubs || ['**'];
      filtered = filterProtostubs(process.env.ENVIRONMENT);
      resourcePath = config.protostubs.repository;
      resourceSrc = config.protostubs.sourceCode;
      break;

    case 'hyperty':
      list = config.hyperties.include_hyperties || ['**'];
      filtered = filterHyperties(process.env.ENVIRONMENT);
      resourcePath = config.hyperties.repository;
      resourceSrc = config.hyperties.sourceCode;
      break;

    case 'dataschema':
      list = config.hyperties.include_hyperties || ['**'];
      filtered = filterDataSchema(process.env.ENVIRONMENT);
      resourcePath = config.hyperties.repository;
      resourceSrc = config.hyperties.sourceCode;
      break;
  }

  if (list.length === 0) {
    list = filtered.map(resource => unixifyPath(resource.dir).replace('/', ''));
  }

  var resources = list.map((item) => {
    item = item.trim();
    return path.join(resourcePath, resourceSrc, item);
  }).filter((item) => {
    return !!filtered.filter(resource => item.includes(resource.dir)).length;
  });

  return resources;
}

/** Create descriptors and source code for Hyperties */

function createHypertiesSourceCode() {
  return generateSourceCode('hyperty');
}

function createHypertiesDescriptors() {
  return generateDescriptor('hyperty');
}

/** Create descriptors and source code for Protostubs */
function createProtoStubsSourceCode() {
  return generateSourceCode('protocolstub');
}

function createProtoStubsDescriptors() {
  return generateDescriptor('protocolstub');
}

/** Create descriptors and source code for IDP Proxies */
function createIDPProxySourceCode() {
  return generateSourceCode('idp-proxy');
}

function createIDPProxyDescriptors() {
  return generateDescriptor('idp-proxy');
}

/** Create descriptors and source code for IDP Proxies */
function createDataSchemasSourceCode() {
  return generateSourceCode('dataschema');
}

function createDataSchemasDescriptors() {
  return generateDescriptor('dataschema');
}

function generateSourceCode(type) {

  var resource = getTypeOfDescriptor(type);
  var list;
  var filtered;
  var repository;
  var convertFunction;

  // console.log('resource:', resource);

  switch (resource.type) {
    case 'idp-proxy':
    case 'protocolstub':
      list = config.protostubs.include_stubs;
      repository = path.join(config.protostubs.repository, config.protostubs.sourceCode);
      filtered = filterProtostubs(process.env.ENVIRONMENT);
      convertFunction = convertProtoStub;
      break;

    case 'hyperty':
      list = config.hyperties.include_hyperties;
      repository = path.join(config.hyperties.repository, config.hyperties.sourceCode);
      filtered = filterHyperties(process.env.ENVIRONMENT);
      convertFunction = convertHyperty;
      break;

    case 'dataschema':
      list = config.hyperties.include_hyperties;
      repository = path.join(config.hyperties.repository, config.hyperties.sourceCode);
      filtered = filterDataSchema(process.env.ENVIRONMENT);
      convertFunction = convertDataSchema;
      break;
  }

  if (list.length === 0) {
    list = filtered.map(resource => unixifyPath(resource.dir).replace('/', ''));
  }

  var sourceCode = list.map((item) => {
    item = item.trim();
    return path.join(repository, item, '*' + resource.extension + '.js');
  }).filter((item) => {
    return !!filtered.filter(resource => item.includes(resource.dir)).length;
  });

  return gulp.src(sourceCode)
    .pipe(convertFunction())
    .on('end', function() {
      browserSync.reload();
    });

}

function generateDescriptor(type, filePath) {

  var resource = getTypeOfDescriptor(type);
  var list;
  var filtered;
  var repository;

  switch (resource.type) {
    case 'idp-proxy':
    case 'protocolstub':
      list = config.protostubs.include_stubs;
      repository = path.join(config.protostubs.repository, config.protostubs.sourceCode);
      filtered = filterProtostubs(process.env.ENVIRONMENT);
      break;

    case 'hyperty':
      list = config.hyperties.include_hyperties;
      repository = path.join(config.hyperties.repository, config.hyperties.sourceCode);
      filtered = filterHyperties(process.env.ENVIRONMENT);
      break;

    case 'dataschema':
      list = config.hyperties.include_hyperties;
      repository = path.join(config.hyperties.repository, config.hyperties.sourceCode);
      filtered = filterDataSchema(process.env.ENVIRONMENT);

  }

  if (list.length === 0) {
    list = filtered.map(resource => unixifyPath(resource.dir).replace('/', ''));
  }

  var descriptor = list.map((item) => {
    item = item.trim();
    return path.join(repository, item, '*' + resource.extension + '.json');
  }).filter((item) => {
    return !!filtered.filter(resource => item.includes(resource.dir)).length;
  });

  if (filePath) { descriptor = filePath; }

  if (resource.type === 'dataschema') {
    descriptor = descriptor.concat('./resources/schemas/**/*.ds.json');
    return gulp.src(descriptor)
      .pipe(convertDataSchema())
      .on('end', function() {
        gutil.log('the pre configuration of ' + resource.type + '  was changed, and the ' + resource.name + '.json was updated');
        browserSync.reload();
      });
  }

  return gulp.src(descriptor)
    .pipe(createDescriptor(resource.type))
    .pipe(gulp.dest('./resources/descriptors/' + resource.name + '.json'))
    .on('end', function() {
      gutil.log('the pre configuration of ' + resource.type + '  was changed, and the ' + resource.name + '.json was updated');
      browserSync.reload();
    });

}

module.exports = {
  createHypertiesSourceCode: createHypertiesSourceCode,
  createHypertiesDescriptors: createHypertiesDescriptors,

  createProtoStubsSourceCode: createProtoStubsSourceCode,
  createProtoStubsDescriptors: createProtoStubsDescriptors,

  createIDPProxySourceCode: createIDPProxySourceCode,
  createIDPProxyDescriptors: createIDPProxyDescriptors,

  createDataSchemasSourceCode: createDataSchemasSourceCode,
  createDataSchemasDescriptors: createDataSchemasDescriptors,

  watchDataSchemas: watchDataSchemas,
  watchIdpProxies: watchIdpProxies,
  watchProtostubs: watchProtostubs,
  watchHyperties: watchHyperties,
  watchResources: watchResources,
  watchHTML: watchHTML
};
<|endoftext|>


<commit_before>'use strict';

const browserSync = require('browser-sync').create('UI5-Server');
const btoa        = require('btoa');
const electron    = require('electron-connect').server.create({ path: 'gulpTaskFiles/electron-auth.js' });
const extend      = require('extend');
const fs          = require('fs');
const proxy       = require('http-proxy-middleware');
const util        = require('gulp-util');

const Promise     = require('promise');
const zipFolder   = Promise.denodeify(require('zip-folder'));
const mkdirp      = Promise.denodeify(require('mkdirp'));
const readFile    = Promise.denodeify(fs.readFile);

/*************************
 * CONFIG
 ************************/
// directory definitions
const { PATHS, readConfig } = require('./CONSTANTS.js');

// sap config
const sapConfig = readConfig();

// unfortunately it is easy to cause a stackoverflow when using String.fromCharCode with a large enough array.
// this function is a workaround for that issue.
// taken from: https://stackoverflow.com/a/9458996/3736051
function arrayBufferToBase64(buffer) {
    let binary = '';
    const bytes = new Uint8Array(buffer);
    const len = bytes.byteLength;
    for (let i = 0; i < len; i++) {
        binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
}

let successReceived = false;
/**
 * Authenticates to the server via electron
 */
function electronAuth(resolveAuth, rejectAuth) {
    electron.start();
    electron.on('auth-success', (authCookie) => {
        // for some reason sometimes the success fires twice
        // this will prevent us from trying to boot two servers on the port
        // todo - debug this
        if (successReceived) {
            return;
        }
        successReceived = true;

        electron.stop();

        resolveAuth(`${authCookie.name}=${authCookie.value}`);
    });

    electron.on('auth-failure', () => {
        rejectAuth('Authentication Failure');
    });
}

/**
 * Fetches the auth tokens, and starts the browsersync server
 */
function server({ useAuth = true, useProxy = true, browserSyncOptions = { } }) {
    const serverStartedPromise = new Promise((resolve, reject) => {
        let authTokenPromise = Promise.resolve('');
        if (useAuth) {
            // get auth data via an electron window, if requested
            authTokenPromise = new Promise(electronAuth);
        }
        authTokenPromise.catch((err) => {
            process.exit(1);
            reject();
            throw err;
        });
        authTokenPromise.then((cookieHeader) => {
            let confirmationHtml;
            let processingHtml;
            let errorHtml;
            readFile('./gulpTaskFiles/confirmation.html', 'utf8').then((text) => {
                confirmationHtml = text;
            });
            readFile('./gulpTaskFiles/processing.html', 'utf8').then((text) => {
                processingHtml = text;
            });
            readFile('./gulpTaskFiles/error.html', 'utf8').then((text) => {
                errorHtml = text;
            });

            // called when the dev goes to http://localhost:<port>/deploy
            // displays a confirmation window for deployment
            const deploymentConfirmation = {
                route: '/deploy',
                handle: (req, res) => {
                    // re-read the config so it can be changed without restarting gulp
                    readConfig();

                    util.log('Request for /deploy received');

                    const html = confirmationHtml.replace('{0}', sapConfig.bspDeployTarget);
                    return res.end(html);
                },
            };
            // called when the dev clicks the confirmation link
            // displays an info window for the deployment process
            const deploymentConfirmed = {
                route: '/deploy/yes',
                handle: (req, res) => {
                    // re-read the config so it can be changed without restarting gulp
                    readConfig();

                    util.log('Request for /deploy/yes received');

                    const zipPath = `./${PATHS.zip}`;
                    util.log(`Making directory "${zipPath}"...`);

                    // create directory
                    return mkdirp(zipPath)
                        .then(() => {
                            util.log('Successful.');

                            // zip the build folder
                            const zipFileName = `${zipPath}/div.zip`;
                            util.log(`Zipping build directory to "${zipFileName}"...`);
                            return zipFolder(PATHS.build.root, zipFileName)
                                .then((err) => {
                                    let html;

                                    if (err) {
                                        const errStr = JSON.stringify(err, null, 4);
                                        util.log(`Error occurred: ${errStr}"`);
                                        // somethign went wrong with zip process
                                        html = errorHtml.replace('{0}', errStr);
                                        res.end(html);
                                    }
                                    util.log('Successful.');

                                    // read the zip file that was just created
                                    util.log('Reading zip file...');
                                    return readFile(zipFileName).then((zipRaw) => {
                                        util.log('Successful.');
                                        // convert from raw bytes to base64
                                        util.log('Converting to base64 string...');
                                        const zipBase64 = arrayBufferToBase64(zipRaw);
                                        util.log('Successful.');
                                        util.log('Returning HTML for processing.');
                                        const data = JSON.stringify({
                                            appName: sapConfig.bspDeployTarget,
                                            zipFile: zipBase64,
                                        });

                                        // the client will do the sending of the requests
                                        html = processingHtml.replace('{0}', JSON.stringify(sapConfig))
                                            .replace('{1}', cookieHeader)
                                            .replace('{2}', data);
                                        res.end(html);
                                    });
                                })
                                .catch((err) => {
                                    const errStr = JSON.stringify(err, null, 4);
                                    util.log(`Error occurred: ${errStr}"`);
                                    res.end(errorHtml.replace('{0}', errStr));
                                });
                        });
                },
            };

            // ordering of middleware matters here as routes are matched in that order
            let middleware = [deploymentConfirmed, deploymentConfirmation];
            if (useProxy) {
                // disable security rejections
                // this is easier than trying to make nodejs accept the ca
                process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
                // setup the middleware to proxy requests to GWD
                const sapProxy = proxy('/sap/', {
                    target: sapConfig.gateway,
                    changeOrigin: true,
                    headers: {
                        Cookie: cookieHeader,
                    },
                    secure: false,
                });
                const libProxy = proxy('**/resources/**', {
                    target: `${sapConfig.gateway}/sap/bc/ui5_ui5/ui2/ushell/shells/abap`,
                    changeOrigin: true,
                    headers: {
                        Cookie: cookieHeader,
                    },
                    secure: false,
                });

                middleware = [
                    sapProxy, libProxy,
                    deploymentConfirmed, deploymentConfirmation,
                ];
            }

            // config and start browsersync
            const opts = extend(browserSyncOptions, {
                server: {
                    baseDir: `./${PATHS.build.root}`,
                    index: 'index.html',
                    middleware,
                },
                online: false,
                port: sapConfig.localDevPort,
                snippetOptions: {
                    blacklist: ['/deploy', '/deploy/yes'],
                },
            });
            browserSync.init(opts);

            // resolve the promise
            resolve();
        });
    });

    return serverStartedPromise;
}

module.exports = server;
<commit_msg>fixed up logic around server params<commit_after>'use strict';

const browserSync = require('browser-sync').create('UI5-Server');
const btoa        = require('btoa');
const electron    = require('electron-connect').server.create({ path: 'gulpTaskFiles/electron-auth.js' });
const extend      = require('extend');
const fs          = require('fs');
const proxy       = require('http-proxy-middleware');
const util        = require('gulp-util');

const Promise     = require('promise');
const zipFolder   = Promise.denodeify(require('zip-folder'));
const mkdirp      = Promise.denodeify(require('mkdirp'));
const readFile    = Promise.denodeify(fs.readFile);

/*************************
 * CONFIG
 ************************/
// directory definitions
const { PATHS, readConfig } = require('./CONSTANTS.js');

// sap config
const sapConfig = readConfig();

// unfortunately it is easy to cause a stackoverflow when using String.fromCharCode with a large enough array.
// this function is a workaround for that issue.
// taken from: https://stackoverflow.com/a/9458996/3736051
function arrayBufferToBase64(buffer) {
    let binary = '';
    const bytes = new Uint8Array(buffer);
    const len = bytes.byteLength;
    for (let i = 0; i < len; i++) {
        binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
}

let successReceived = false;
/**
 * Authenticates to the server via electron
 */
function electronAuth(resolveAuth, rejectAuth) {
    electron.start();
    electron.on('auth-success', (authCookie) => {
        // for some reason sometimes the success fires twice
        // this will prevent us from trying to boot two servers on the port
        // todo - debug this
        if (successReceived) {
            return;
        }
        successReceived = true;

        electron.stop();

        resolveAuth(`${authCookie.name}=${authCookie.value}`);
    });

    electron.on('auth-failure', () => {
        rejectAuth('Authentication Failure');
    });
}

/**
 * Fetches the auth tokens, and starts the browsersync server
 */
function server({ useAuth = true, useProxy = true, browserSyncOptions = { } }) {
    if (!useProxy) {
        // no point using auth if we don't have the proxy!
        useAuth = false;
    }

    const serverStartedPromise = new Promise((resolve, reject) => {
        let authTokenPromise = Promise.resolve('');
        if (useAuth) {
            // get auth data via an electron window, if requested
            authTokenPromise = new Promise(electronAuth);
        }
        authTokenPromise.catch((err) => {
            process.exit(1);
            reject();
            throw err;
        });
        authTokenPromise.then((cookieHeader) => {
            let middleware = [];
            if (useProxy) {
                let confirmationHtml;
                let processingHtml;
                let errorHtml;
                readFile('./gulpTaskFiles/confirmation.html', 'utf8').then((text) => {
                    confirmationHtml = text;
                });
                readFile('./gulpTaskFiles/processing.html', 'utf8').then((text) => {
                    processingHtml = text;
                });
                readFile('./gulpTaskFiles/error.html', 'utf8').then((text) => {
                    errorHtml = text;
                });

                // called when the dev goes to http://localhost:<port>/deploy
                // displays a confirmation window for deployment
                const deploymentConfirmation = {
                    route: '/deploy',
                    handle: (req, res) => {
                        // re-read the config so it can be changed without restarting gulp
                        readConfig();

                        util.log('Request for /deploy received');

                        const html = confirmationHtml.replace('{0}', sapConfig.bspDeployTarget);
                        return res.end(html);
                    },
                };
                // called when the dev clicks the confirmation link
                // displays an info window for the deployment process
                const deploymentConfirmed = {
                    route: '/deploy/yes',
                    handle: (req, res) => {
                        // re-read the config so it can be changed without restarting gulp
                        readConfig();

                        util.log('Request for /deploy/yes received');

                        const zipPath = `./${PATHS.zip}`;
                        util.log(`Making directory "${zipPath}"...`);

                        // create directory
                        return mkdirp(zipPath)
                            .then(() => {
                                util.log('Successful.');

                                // zip the build folder
                                const zipFileName = `${zipPath}/div.zip`;
                                util.log(`Zipping build directory to "${zipFileName}"...`);
                                return zipFolder(PATHS.build.root, zipFileName)
                                    .then((err) => {
                                        let html;

                                        if (err) {
                                            const errStr = JSON.stringify(err, null, 4);
                                            util.log(`Error occurred: ${errStr}"`);
                                            // somethign went wrong with zip process
                                            html = errorHtml.replace('{0}', errStr);
                                            res.end(html);
                                        }
                                        util.log('Successful.');

                                        // read the zip file that was just created
                                        util.log('Reading zip file...');
                                        return readFile(zipFileName).then((zipRaw) => {
                                            util.log('Successful.');
                                            // convert from raw bytes to base64
                                            util.log('Converting to base64 string...');
                                            const zipBase64 = arrayBufferToBase64(zipRaw);
                                            util.log('Successful.');
                                            util.log('Returning HTML for processing.');
                                            const data = JSON.stringify({
                                                appName: sapConfig.bspDeployTarget,
                                                zipFile: zipBase64,
                                            });

                                            // the client will do the sending of the requests
                                            html = processingHtml.replace('{0}', JSON.stringify(sapConfig))
                                                .replace('{1}', cookieHeader)
                                                .replace('{2}', data);
                                            res.end(html);
                                        });
                                    })
                                    .catch((err) => {
                                        const errStr = JSON.stringify(err, null, 4);
                                        util.log(`Error occurred: ${errStr}"`);
                                        res.end(errorHtml.replace('{0}', errStr));
                                    });
                            });
                    },
                };

                // disable security rejections
                // this is easier than trying to make nodejs accept the ca
                process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
                // setup the middleware to proxy requests to GWD
                const sapProxy = proxy('/sap/', {
                    target: sapConfig.gateway,
                    changeOrigin: true,
                    headers: {
                        Cookie: cookieHeader,
                    },
                    secure: false,
                });
                const libProxy = proxy('**/resources/**', {
                    target: `${sapConfig.gateway}/sap/bc/ui5_ui5/ui2/ushell/shells/abap`,
                    changeOrigin: true,
                    headers: {
                        Cookie: cookieHeader,
                    },
                    secure: false,
                });

                middleware = [
                    sapProxy, libProxy,
                    // ordering of middleware matters here as routes are matched in that order
                    deploymentConfirmed, deploymentConfirmation,
                ];
            }

            // config and start browsersync
            const opts = extend({}, browserSyncOptions, {
                server: {
                    baseDir: `./${PATHS.build.root}`,
                    index: 'index.html',
                    middleware,
                },
                online: false,
                port: sapConfig.localDevPort,
                snippetOptions: {
                    blacklist: ['/deploy', '/deploy/yes'],
                },
            });
            browserSync.init(opts);

            // resolve the promise
            resolve();
        });
    });

    return serverStartedPromise;
}

module.exports = server;
<|endoftext|>