'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }

var _regeneratorRuntime = _interopDefault(require('babel-runtime/regenerator'));
var _asyncToGenerator = _interopDefault(require('babel-runtime/helpers/asyncToGenerator'));
var pify = _interopDefault(require('pify'));
var _Promise = _interopDefault(require('babel-runtime/core-js/promise'));
var _getIterator = _interopDefault(require('babel-runtime/core-js/get-iterator'));
var _Map = _interopDefault(require('babel-runtime/core-js/map'));
var path = _interopDefault(require('path'));

var fs;
var fs$1 = function () {
  return fs;
};
function setfs(newFs) {
  fs = newFs;
}

var rm = function () {
  var _ref = _asyncToGenerator(_regeneratorRuntime.mark(function _callee(filepath) {
    return _regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            _context.prev = 0;
            _context.next = 3;
            return pify(fs$1().unlink)(filepath);

          case 3:
            _context.next = 9;
            break;

          case 5:
            _context.prev = 5;
            _context.t0 = _context['catch'](0);

            if (!(_context.t0.code !== 'ENOENT')) {
              _context.next = 9;
              break;
            }

            throw _context.t0;

          case 9:
          case 'end':
            return _context.stop();
        }
      }
    }, _callee, this, [[0, 5]]);
  }));

  return function rm(_x) {
    return _ref.apply(this, arguments);
  };
}();

// An async exists variant
var exists = function () {
  var _ref = _asyncToGenerator(_regeneratorRuntime.mark(function _callee(file, options) {
    return _regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            return _context.abrupt('return', new _Promise(function (resolve, reject) {
              fs$1().stat(file, function (err, stats) {
                if (err) return err.code === 'ENOENT' ? resolve(false) : reject(err);
                resolve(true);
              });
            }));

          case 1:
          case 'end':
            return _context.stop();
        }
      }
    }, _callee, this);
  }));

  return function exists(_x, _x2) {
    return _ref.apply(this, arguments);
  };
}();

// @flow
/*::
type Node = {
  type: string,
  fullpath: string,
  basename: string,
  metadata: Object, // mode, oid
  parent?: Node,
  children: Array<Node>
}
*/

function flatFileListToDirectoryStructure(files /*: Array<{path: string}> */
) /*: Node|void */{
  var inodes /*: Map<string, Node> */ = new _Map();
  var mkdir = function mkdir(name) /*: Node|void */{
    if (!inodes.has(name)) {
      var dir /*: Node */ = {
        type: 'tree',
        fullpath: name,
        basename: path.basename(name),
        metadata: {},
        children: []
      };
      inodes.set(name, dir);
      // This recursively generates any missing parent folders.
      // We do it after we've added the inode to the set so that
      // we don't recurse infinitely trying to create the root '.' dirname.
      dir.parent = mkdir(path.dirname(name));
      if (dir.parent && dir.parent !== dir) dir.parent.children.push(dir);
    }
    return inodes.get(name);
  };

  var mkfile = function mkfile(name, metadata) /*: Node|void */{
    if (!inodes.has(name)) {
      var file /*: Node */ = {
        type: 'blob',
        fullpath: name,
        basename: path.basename(name),
        metadata: metadata,
        // This recursively generates any missing parent folders.
        parent: mkdir(path.dirname(name)),
        children: []
      };
      if (file.parent) file.parent.children.push(file);
      inodes.set(name, file);
    }
    return inodes.get(name);
  };

  var _iteratorNormalCompletion = true;
  var _didIteratorError = false;
  var _iteratorError = undefined;

  try {
    for (var _iterator = _getIterator(files), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
      var file = _step.value;

      mkfile(file.path, file);
    }
  } catch (err) {
    _didIteratorError = true;
    _iteratorError = err;
  } finally {
    try {
      if (!_iteratorNormalCompletion && _iterator.return) {
        _iterator.return();
      }
    } finally {
      if (_didIteratorError) {
        throw _iteratorError;
      }
    }
  }

  return inodes.get('.');
}

var sleep = function () {
  var _ref = _asyncToGenerator(_regeneratorRuntime.mark(function _callee(ms) {
    return _regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            return _context.abrupt("return", new _Promise(function (resolve, reject) {
              return setTimeout(resolve, ms);
            }));

          case 1:
          case "end":
            return _context.stop();
        }
      }
    }, _callee, this);
  }));

  return function sleep(_x) {
    return _ref.apply(this, arguments);
  };
}();

// @flow
// This is modeled after the lockfile strategy used by the git source code.
var delayedReleases = new _Map();

var lock = function () {
  var _ref = _asyncToGenerator(_regeneratorRuntime.mark(function _callee(filename /*: string */
  ) {
    var triesLeft /*: number */ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3;
    return _regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            if (!delayedReleases.has(filename)) {
              _context.next = 4;
              break;
            }

            clearTimeout(delayedReleases.get(filename));
            delayedReleases.delete(filename);
            return _context.abrupt('return');

          case 4:
            if (!(triesLeft === 0)) {
              _context.next = 6;
              break;
            }

            throw new Error('Unable to acquire lockfile \'' + filename + '\'. Exhausted tries.');

          case 6:
            _context.prev = 6;
            _context.next = 9;
            return pify(fs$1().mkdir)(filename + '.lock');

          case 9:
            _context.next = 18;
            break;

          case 11:
            _context.prev = 11;
            _context.t0 = _context['catch'](6);

            if (!(_context.t0.code === 'EEXIST')) {
              _context.next = 18;
              break;
            }

            _context.next = 16;
            return sleep(100);

          case 16:
            _context.next = 18;
            return lock(filename, triesLeft - 1);

          case 18:
          case 'end':
            return _context.stop();
        }
      }
    }, _callee, this, [[6, 11]]);
  }));

  return function lock(_x2) {
    return _ref.apply(this, arguments);
  };
}();

var unlock = function () {
  var _ref2 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee3(filename /*: string */
  ) {
    var _this = this;

    return _regeneratorRuntime.wrap(function _callee3$(_context3) {
      while (1) {
        switch (_context3.prev = _context3.next) {
          case 0:
            if (!delayedReleases.has(filename)) {
              _context3.next = 2;
              break;
            }

            throw new Error('Cannot double-release lockfile');

          case 2:
            // Basically, we lie and say it was deleted ASAP.
            // But really we wait a bit to see if you want to acquire it again.
            delayedReleases.set(filename, setTimeout(_asyncToGenerator(_regeneratorRuntime.mark(function _callee2() {
              return _regeneratorRuntime.wrap(function _callee2$(_context2) {
                while (1) {
                  switch (_context2.prev = _context2.next) {
                    case 0:
                      delayedReleases.delete(filename);
                      _context2.next = 3;
                      return pify(fs$1().rmdir)(filename + '.lock');

                    case 3:
                    case 'end':
                      return _context2.stop();
                  }
                }
              }, _callee2, _this);
            }))));

          case 3:
          case 'end':
            return _context3.stop();
        }
      }
    }, _callee3, this);
  }));

  return function unlock(_x4) {
    return _ref2.apply(this, arguments);
  };
}();

// @flow
var mkdir = function () {
  var _ref = _asyncToGenerator(_regeneratorRuntime.mark(function _callee(dirpath /*: string */) {
    var parent;
    return _regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            _context.prev = 0;
            _context.next = 3;
            return pify(fs$1().mkdir)(dirpath);

          case 3:
            return _context.abrupt('return');

          case 6:
            _context.prev = 6;
            _context.t0 = _context['catch'](0);

            if (!(_context.t0 === null)) {
              _context.next = 10;
              break;
            }

            return _context.abrupt('return');

          case 10:
            if (!(_context.t0.code === 'EEXIST')) {
              _context.next = 12;
              break;
            }

            return _context.abrupt('return');

          case 12:
            if (!(_context.t0.code === 'ENOENT')) {
              _context.next = 20;
              break;
            }

            parent = path.dirname(dirpath);
            // Check to see if we've gone too far

            if (!(parent === '.' || parent === '/' || parent === dirpath)) {
              _context.next = 16;
              break;
            }

            throw _context.t0;

          case 16:
            _context.next = 18;
            return mkdir(parent);

          case 18:
            _context.next = 20;
            return mkdir(dirpath);

          case 20:
          case 'end':
            return _context.stop();
        }
      }
    }, _callee, this, [[0, 6]]);
  }));

  return function mkdir(_x) {
    return _ref.apply(this, arguments);
  };
}();

var mkdirs = function () {
  var _ref2 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee2(dirlist /*: string[] */) {
    return _regeneratorRuntime.wrap(function _callee2$(_context2) {
      while (1) {
        switch (_context2.prev = _context2.next) {
          case 0:
            return _context2.abrupt('return', _Promise.all(dirlist.map(mkdir)));

          case 1:
          case 'end':
            return _context2.stop();
        }
      }
    }, _callee2, this);
  }));

  return function mkdirs(_x2) {
    return _ref2.apply(this, arguments);
  };
}();

// An async readFile variant that returns null instead of throwing errors
var read = function () {
  var _ref = _asyncToGenerator(_regeneratorRuntime.mark(function _callee(file, options) {
    return _regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            return _context.abrupt('return', new _Promise(function (resolve, reject) {
              fs$1().readFile(file, options, function (err, file) {
                return err ? resolve(null) : resolve(file);
              });
            }));

          case 1:
          case 'end':
            return _context.stop();
        }
      }
    }, _callee, this);
  }));

  return function read(_x, _x2) {
    return _ref.apply(this, arguments);
  };
}();

// @flow
// An async writeFile variant that automatically creates missing directories,
// and returns null instead of throwing errors.
var write = function () {
  var _ref = _asyncToGenerator(_regeneratorRuntime.mark(function _callee(filepath /*: string */
  , contents /*: string|Buffer */
  ) {
    var options /*: Object */ = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    return _regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            _context.prev = 0;
            _context.next = 3;
            return pify(fs$1().writeFile)(filepath, contents, options);

          case 3:
            return _context.abrupt('return');

          case 6:
            _context.prev = 6;
            _context.t0 = _context['catch'](0);
            _context.next = 10;
            return mkdir(path.dirname(filepath));

          case 10:
            _context.next = 12;
            return pify(fs$1().writeFile)(filepath, contents, options);

          case 12:
          case 'end':
            return _context.stop();
        }
      }
    }, _callee, this, [[0, 6]]);
  }));

  return function write(_x2, _x3) {
    return _ref.apply(this, arguments);
  };
}();

var name = "isomorphic-git";
var version = "0.0.0-development";
var description = "A pure JavaScript implementation of git for node and browsers!";
var typings = "./src/index.d.ts";
var main = "dist/for-node/";
var browser = "dist/for-browserify/";
var module$1 = "dist/for-future/";
var unpkg = "dist/bundle.umd.min.js";
var bin = { "isogit": "./cli.js" };
var engines = { "node": ">=7.6.0" };
var scripts = { "start": "nps", "test": "nps test", "precommit": "nps format toc", "semantic-release": "semantic-release pre && npm publish && semantic-release post" };
var repository = { "type": "git", "url": "https://github.com/wmhilton/isomorphic-git.git" };
var keywords = ["git"];
var author = "William Hilton <wmhilton@gmail.com>";
var license = "Unlicense";
var bugs = { "url": "https://github.com/wmhilton/isomorphic-git/issues" };
var homepage = "https://github.com/wmhilton/isomorphic-git#readme";
var files = ["dist", "cli.js"];
var dependencies = { "async-lock": "^1.0.0", "await-stream-ready": "^1.0.1", "babel-runtime": "^6.26.0", "buffer": "^5.0.7", "buffer-peek-stream": "^1.0.1", "buffercursor": "0.0.12", "gartal": "^1.1.2", "git-apply-delta": "0.0.7", "git-list-pack": "0.0.10", "ignore": "^3.3.6", "ini": "^1.3.4", "marky": "^1.2.0", "minimisted": "^2.0.0", "openpgp": "^2.5.10", "pad": "^2.0.1", "pako": "^1.0.5", "pify": "^3.0.0", "shasum": "^1.0.2", "simple-concat": "^1.0.0", "simple-get": "^2.7.0", "through2": "^2.0.3" };
var devDependencies = { "babel-plugin-external-helpers": "^6.22.0", "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", "babel-plugin-transform-object-rest-spread": "^6.23.0", "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-env": "^1.6.0", "babel-preset-flow": "^6.23.0", "ban-sensitive-files": "^1.9.0", "browserfs": "^1.4.3", "browserify": "^14.4.0", "browserify-shim": "^3.8.14", "codecov": "^3.0.0", "doctoc": "^1.3.0", "esdoc": "^1.0.4", "esdoc-ecmascript-proposal-plugin": "^1.0.0", "esdoc-importpath-plugin": "^1.0.1", "esdoc-standard-plugin": "^1.0.0", "husky": "^0.14.3", "jest": "^21.2.1", "jest-fixtures": "^0.6.0", "jsonfile": "^4.0.0", "karma": "^1.7.1", "karma-browserify": "^5.1.1", "karma-chrome-launcher": "^2.2.0", "karma-firefox-launcher": "^1.0.1", "karma-sauce-launcher": "^1.2.0", "karma-tap": "^3.1.1", "lodash": "^4.17.4", "nock": "^9.0.17", "npm-run-all": "^4.1.1", "nps": "^5.7.1", "nps-utils": "^1.4.0", "parse-header-stream": "^1.1.1", "prettier-standard": "^7.0.3", "rollup": "^0.51.6", "rollup-plugin-babel": "^3.0.2", "rollup-plugin-json": "^2.3.0", "standard": "^10.0.3", "stream-equal": "^1.0.1", "tape": "^4.8.0", "uglify-es": "^3.1.2", "watch": "^1.0.2", "watchify": "^3.9.0", "semantic-release": "^8.2.0" };
var ava = { "source": ["dist/for-node/*"] };
var browserify = { "transform": ["browserify-shim"] };
var testling = { "files": "testling/basic-test.js", "browsers": ["chrome/latest", "firefox/latest", "ie/latest"] };
var jest = { "testPathIgnorePatterns": ["__helpers__"], "testEnvironment": "node" };
var _package = {
	name: name,
	version: version,
	description: description,
	typings: typings,
	main: main,
	browser: browser,
	module: module$1,
	unpkg: unpkg,
	bin: bin,
	engines: engines,
	scripts: scripts,
	repository: repository,
	keywords: keywords,
	author: author,
	license: license,
	bugs: bugs,
	homepage: homepage,
	files: files,
	dependencies: dependencies,
	devDependencies: devDependencies,
	ava: ava,
	browserify: browserify,
	testling: testling,
	jest: jest,
	"browserify-shim": { "fs": "global:fs" }
};

var _package$1 = Object.freeze({
	name: name,
	version: version,
	description: description,
	typings: typings,
	main: main,
	browser: browser,
	module: module$1,
	unpkg: unpkg,
	bin: bin,
	engines: engines,
	scripts: scripts,
	repository: repository,
	keywords: keywords,
	author: author,
	license: license,
	bugs: bugs,
	homepage: homepage,
	files: files,
	dependencies: dependencies,
	devDependencies: devDependencies,
	ava: ava,
	browserify: browserify,
	testling: testling,
	jest: jest,
	default: _package
});

/**
 * @param {string} company
 * @param {string} token
 * @returns {username, password}
 *
 * Use with {@link #gitpush .push} and {@link #gitpull .pull} to set Basic Authentication headers.
 * This for is for *actual* OAuth2 tokens (not "personal access tokens").
 * Unfortunately, all the major git hosting companies have chosen different conventions!
 * Lucky for you, I already looked up and codified it for you.
 *
 * - oauth2('github', token) - Github uses `token` as the username, and 'x-oauth-basic' as the password.
 * - oauth2('bitbucket', token) - Bitbucket uses 'x-token-auth' as the username, and `token` as the password.
 * - oauth2('gitlab', token) - Gitlab uses 'oauth2' as the username, and `token` as the password.
 *
 * I will gladly accept pull requests for more companies' conventions.
 */
function oauth2(company, token) {
  switch (company) {
    case 'github':
      return {
        username: token,
        password: 'x-oauth-basic'
      };
    case 'bitbucket':
      return {
        username: 'x-token-auth',
        password: token
      };
    case 'gitlab':
      return {
        username: 'oauth2',
        password: token
      };
    default:
      throw new Error('I don\'t know how ' + company + ' expects its Basic Auth headers to be formatted for OAuth2 usage. If you do, you can use the regular \'.auth(username, password)\' to set the basic auth header yourself.');
  }
}

/**
 *
 * Use with {@link #gitpush .push} and {@link #gitpull .pull} to set Basic Authentication headers.
 * This works for basic username / password auth, or the newer username / token auth
 * that is often required if 2FA is enabled.
 *
 * Authentication is normally required for pushing to a git repository.
 * It may also be required to clone or fetch from a private repository.
 * Git does all its authentication using HTTPS Basic Authentication.
 * Usually this is straightforward, but there are some things to watch out for.
 *
 * If you have two-factor authentication (2FA) enabled on your account, you
 * probably cannot push or pull using your regular username and password.
 * Instead, you may have to create a Personal Access Token (or an App
 * Password in Bitbucket lingo) and use that to authenticate.
 *
 * @param {string} username
 * @param {string} password
 * @returns {username, password}
 *
 * @example
 * let {username, password} = auth('username', 'password')
 *
 * // a one-argument version is also supported
 * let {username, password} = auth('username:password')
 *
 * // Personal Access Token Authentication
 * // (note Bitbucket calls theirs App Passwords instead for some reason)
 * let {username, password} = auth('username', 'personal access token')
 * let {username, password} = auth('username', 'app password')
 * let {username, password} = auth('personal access token') // Github (only) lets you leave out the username
 */
function auth(username, password) {
  // Allow specifying it as one argument (mostly for CLI inputability)
  if (password === undefined) {
    var i = username.indexOf(':');
    if (i > -1) {
      password = username.slice(i + 1);
      username = username.slice(0, i);
    } else {
      password = ''; // Enables the .auth(GITHUB_TOKEN) no-username shorthand
    }
  }
  return { username: username, password: password };
}

exports.rm = rm;
exports.exists = exists;
exports.flatFileListToDirectoryStructure = flatFileListToDirectoryStructure;
exports.fs = fs$1;
exports.setfs = setfs;
exports.lock = lock;
exports.unlock = unlock;
exports.mkdirs = mkdirs;
exports.read = read;
exports.sleep = sleep;
exports.write = write;
exports.pkg = _package$1;
exports.oauth2 = oauth2;
exports.auth = auth;
//# sourceMappingURL=utils.js.map
