|
|
|
|
module.exports =
|
|
|
|
|
/******/ (function(modules, runtime) { // webpackBootstrap
|
|
|
|
|
/******/ "use strict";
|
|
|
|
|
/******/ // The module cache
|
|
|
|
|
/******/ var installedModules = {};
|
|
|
|
|
/******/
|
|
|
|
|
/******/ // The require function
|
|
|
|
|
/******/ function __webpack_require__(moduleId) {
|
|
|
|
|
/******/
|
|
|
|
|
/******/ // Check if module is in cache
|
|
|
|
|
/******/ if(installedModules[moduleId]) {
|
|
|
|
|
/******/ return installedModules[moduleId].exports;
|
|
|
|
|
/******/ }
|
|
|
|
|
/******/ // Create a new module (and put it into the cache)
|
|
|
|
|
/******/ var module = installedModules[moduleId] = {
|
|
|
|
|
/******/ i: moduleId,
|
|
|
|
|
/******/ l: false,
|
|
|
|
|
/******/ exports: {}
|
|
|
|
|
/******/ };
|
|
|
|
|
/******/
|
|
|
|
|
/******/ // Execute the module function
|
|
|
|
|
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
|
|
|
|
/******/
|
|
|
|
|
/******/ // Flag the module as loaded
|
|
|
|
|
/******/ module.l = true;
|
|
|
|
|
/******/
|
|
|
|
|
/******/ // Return the exports of the module
|
|
|
|
|
/******/ return module.exports;
|
|
|
|
|
/******/ }
|
|
|
|
|
/******/
|
|
|
|
|
/******/
|
|
|
|
|
/******/ __webpack_require__.ab = __dirname + "/";
|
|
|
|
|
/******/
|
|
|
|
|
/******/ // the startup function
|
|
|
|
|
/******/ function startup() {
|
|
|
|
|
/******/ // Load entry module and return exports
|
|
|
|
|
/******/ return __webpack_require__(681);
|
|
|
|
|
/******/ };
|
|
|
|
|
/******/
|
|
|
|
|
/******/ // run startup
|
|
|
|
|
/******/ return startup();
|
|
|
|
|
/******/ })
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
/******/ ({
|
|
|
|
|
|
|
|
|
|
/***/ 1:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
|
|
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
|
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
|
|
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
|
|
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
|
|
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
|
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
const childProcess = __webpack_require__(129);
|
|
|
|
|
const path = __webpack_require__(622);
|
|
|
|
|
const util_1 = __webpack_require__(669);
|
|
|
|
|
const ioUtil = __webpack_require__(672);
|
|
|
|
|
const exec = util_1.promisify(childProcess.exec);
|
|
|
|
|
/**
|
|
|
|
|
* Copies a file or folder.
|
|
|
|
|
* Based off of shelljs - https://github.com/shelljs/shelljs/blob/9237f66c52e5daa40458f94f9565e18e8132f5a6/src/cp.js
|
|
|
|
|
*
|
|
|
|
|
* @param source source path
|
|
|
|
|
* @param dest destination path
|
|
|
|
|
* @param options optional. See CopyOptions.
|
|
|
|
|
*/
|
|
|
|
|
function cp(source, dest, options = {}) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
const { force, recursive } = readCopyOptions(options);
|
|
|
|
|
const destStat = (yield ioUtil.exists(dest)) ? yield ioUtil.stat(dest) : null;
|
|
|
|
|
// Dest is an existing file, but not forcing
|
|
|
|
|
if (destStat && destStat.isFile() && !force) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// If dest is an existing directory, should copy inside.
|
|
|
|
|
const newDest = destStat && destStat.isDirectory()
|
|
|
|
|
? path.join(dest, path.basename(source))
|
|
|
|
|
: dest;
|
|
|
|
|
if (!(yield ioUtil.exists(source))) {
|
|
|
|
|
throw new Error(`no such file or directory: ${source}`);
|
|
|
|
|
}
|
|
|
|
|
const sourceStat = yield ioUtil.stat(source);
|
|
|
|
|
if (sourceStat.isDirectory()) {
|
|
|
|
|
if (!recursive) {
|
|
|
|
|
throw new Error(`Failed to copy. ${source} is a directory, but tried to copy without recursive flag.`);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
yield cpDirRecursive(source, newDest, 0, force);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (path.relative(source, newDest) === '') {
|
|
|
|
|
// a file cannot be copied to itself
|
|
|
|
|
throw new Error(`'${newDest}' and '${source}' are the same file`);
|
|
|
|
|
}
|
|
|
|
|
yield copyFile(source, newDest, force);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.cp = cp;
|
|
|
|
|
/**
|
|
|
|
|
* Moves a path.
|
|
|
|
|
*
|
|
|
|
|
* @param source source path
|
|
|
|
|
* @param dest destination path
|
|
|
|
|
* @param options optional. See MoveOptions.
|
|
|
|
|
*/
|
|
|
|
|
function mv(source, dest, options = {}) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
if (yield ioUtil.exists(dest)) {
|
|
|
|
|
let destExists = true;
|
|
|
|
|
if (yield ioUtil.isDirectory(dest)) {
|
|
|
|
|
// If dest is directory copy src into dest
|
|
|
|
|
dest = path.join(dest, path.basename(source));
|
|
|
|
|
destExists = yield ioUtil.exists(dest);
|
|
|
|
|
}
|
|
|
|
|
if (destExists) {
|
|
|
|
|
if (options.force == null || options.force) {
|
|
|
|
|
yield rmRF(dest);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
throw new Error('Destination already exists');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
yield mkdirP(path.dirname(dest));
|
|
|
|
|
yield ioUtil.rename(source, dest);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.mv = mv;
|
|
|
|
|
/**
|
|
|
|
|
* Remove a path recursively with force
|
|
|
|
|
*
|
|
|
|
|
* @param inputPath path to remove
|
|
|
|
|
*/
|
|
|
|
|
function rmRF(inputPath) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
if (ioUtil.IS_WINDOWS) {
|
|
|
|
|
// Node doesn't provide a delete operation, only an unlink function. This means that if the file is being used by another
|
|
|
|
|
// program (e.g. antivirus), it won't be deleted. To address this, we shell out the work to rd/del.
|
|
|
|
|
try {
|
|
|
|
|
if (yield ioUtil.isDirectory(inputPath, true)) {
|
|
|
|
|
yield exec(`rd /s /q "${inputPath}"`);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
yield exec(`del /f /a "${inputPath}"`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
|
|
|
|
// if you try to delete a file that doesn't exist, desired result is achieved
|
|
|
|
|
// other errors are valid
|
|
|
|
|
if (err.code !== 'ENOENT')
|
|
|
|
|
throw err;
|
|
|
|
|
}
|
|
|
|
|
// Shelling out fails to remove a symlink folder with missing source, this unlink catches that
|
|
|
|
|
try {
|
|
|
|
|
yield ioUtil.unlink(inputPath);
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
|
|
|
|
// if you try to delete a file that doesn't exist, desired result is achieved
|
|
|
|
|
// other errors are valid
|
|
|
|
|
if (err.code !== 'ENOENT')
|
|
|
|
|
throw err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
let isDir = false;
|
|
|
|
|
try {
|
|
|
|
|
isDir = yield ioUtil.isDirectory(inputPath);
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
|
|
|
|
// if you try to delete a file that doesn't exist, desired result is achieved
|
|
|
|
|
// other errors are valid
|
|
|
|
|
if (err.code !== 'ENOENT')
|
|
|
|
|
throw err;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (isDir) {
|
|
|
|
|
yield exec(`rm -rf "${inputPath}"`);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
yield ioUtil.unlink(inputPath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.rmRF = rmRF;
|
|
|
|
|
/**
|
|
|
|
|
* Make a directory. Creates the full path with folders in between
|
|
|
|
|
* Will throw if it fails
|
|
|
|
|
*
|
|
|
|
|
* @param fsPath path to create
|
|
|
|
|
* @returns Promise<void>
|
|
|
|
|
*/
|
|
|
|
|
function mkdirP(fsPath) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
yield ioUtil.mkdirP(fsPath);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.mkdirP = mkdirP;
|
|
|
|
|
/**
|
|
|
|
|
* Returns path of a tool had the tool actually been invoked. Resolves via paths.
|
|
|
|
|
* If you check and the tool does not exist, it will throw.
|
|
|
|
|
*
|
|
|
|
|
* @param tool name of the tool
|
|
|
|
|
* @param check whether to check if tool exists
|
|
|
|
|
* @returns Promise<string> path to tool
|
|
|
|
|
*/
|
|
|
|
|
function which(tool, check) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
if (!tool) {
|
|
|
|
|
throw new Error("parameter 'tool' is required");
|
|
|
|
|
}
|
|
|
|
|
// recursive when check=true
|
|
|
|
|
if (check) {
|
|
|
|
|
const result = yield which(tool, false);
|
|
|
|
|
if (!result) {
|
|
|
|
|
if (ioUtil.IS_WINDOWS) {
|
|
|
|
|
throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also verify the file has a valid extension for an executable file.`);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable.`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
// build the list of extensions to try
|
|
|
|
|
const extensions = [];
|
|
|
|
|
if (ioUtil.IS_WINDOWS && process.env.PATHEXT) {
|
|
|
|
|
for (const extension of process.env.PATHEXT.split(path.delimiter)) {
|
|
|
|
|
if (extension) {
|
|
|
|
|
extensions.push(extension);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// if it's rooted, return it if exists. otherwise return empty.
|
|
|
|
|
if (ioUtil.isRooted(tool)) {
|
|
|
|
|
const filePath = yield ioUtil.tryGetExecutablePath(tool, extensions);
|
|
|
|
|
if (filePath) {
|
|
|
|
|
return filePath;
|
|
|
|
|
}
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
// if any path separators, return empty
|
|
|
|
|
if (tool.includes('/') || (ioUtil.IS_WINDOWS && tool.includes('\\'))) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
// build the list of directories
|
|
|
|
|
//
|
|
|
|
|
// Note, technically "where" checks the current directory on Windows. From a toolkit perspective,
|
|
|
|
|
// it feels like we should not do this. Checking the current directory seems like more of a use
|
|
|
|
|
// case of a shell, and the which() function exposed by the toolkit should strive for consistency
|
|
|
|
|
// across platforms.
|
|
|
|
|
const directories = [];
|
|
|
|
|
if (process.env.PATH) {
|
|
|
|
|
for (const p of process.env.PATH.split(path.delimiter)) {
|
|
|
|
|
if (p) {
|
|
|
|
|
directories.push(p);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// return the first match
|
|
|
|
|
for (const directory of directories) {
|
|
|
|
|
const filePath = yield ioUtil.tryGetExecutablePath(directory + path.sep + tool, extensions);
|
|
|
|
|
if (filePath) {
|
|
|
|
|
return filePath;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
|
|
|
|
throw new Error(`which failed with message ${err.message}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.which = which;
|
|
|
|
|
function readCopyOptions(options) {
|
|
|
|
|
const force = options.force == null ? true : options.force;
|
|
|
|
|
const recursive = Boolean(options.recursive);
|
|
|
|
|
return { force, recursive };
|
|
|
|
|
}
|
|
|
|
|
function cpDirRecursive(sourceDir, destDir, currentDepth, force) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
// Ensure there is not a run away recursive copy
|
|
|
|
|
if (currentDepth >= 255)
|
|
|
|
|
return;
|
|
|
|
|
currentDepth++;
|
|
|
|
|
yield mkdirP(destDir);
|
|
|
|
|
const files = yield ioUtil.readdir(sourceDir);
|
|
|
|
|
for (const fileName of files) {
|
|
|
|
|
const srcFile = `${sourceDir}/${fileName}`;
|
|
|
|
|
const destFile = `${destDir}/${fileName}`;
|
|
|
|
|
const srcFileStat = yield ioUtil.lstat(srcFile);
|
|
|
|
|
if (srcFileStat.isDirectory()) {
|
|
|
|
|
// Recurse
|
|
|
|
|
yield cpDirRecursive(srcFile, destFile, currentDepth, force);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
yield copyFile(srcFile, destFile, force);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Change the mode for the newly created directory
|
|
|
|
|
yield ioUtil.chmod(destDir, (yield ioUtil.stat(sourceDir)).mode);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// Buffered file copy
|
|
|
|
|
function copyFile(srcFile, destFile, force) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
if ((yield ioUtil.lstat(srcFile)).isSymbolicLink()) {
|
|
|
|
|
// unlink/re-link it
|
|
|
|
|
try {
|
|
|
|
|
yield ioUtil.lstat(destFile);
|
|
|
|
|
yield ioUtil.unlink(destFile);
|
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
// Try to override file permission
|
|
|
|
|
if (e.code === 'EPERM') {
|
|
|
|
|
yield ioUtil.chmod(destFile, '0666');
|
|
|
|
|
yield ioUtil.unlink(destFile);
|
|
|
|
|
}
|
|
|
|
|
// other errors = it doesn't exist, no work to do
|
|
|
|
|
}
|
|
|
|
|
// Copy over symlink
|
|
|
|
|
const symlinkFull = yield ioUtil.readlink(srcFile);
|
|
|
|
|
yield ioUtil.symlink(symlinkFull, destFile, ioUtil.IS_WINDOWS ? 'junction' : null);
|
|
|
|
|
}
|
|
|
|
|
else if (!(yield ioUtil.exists(destFile)) || force) {
|
|
|
|
|
yield ioUtil.copyFile(srcFile, destFile);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
//# sourceMappingURL=io.js.map
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 9:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
|
|
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
|
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
|
|
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
|
|
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
|
|
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
|
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
const os = __webpack_require__(87);
|
|
|
|
|
const events = __webpack_require__(614);
|
|
|
|
|
const child = __webpack_require__(129);
|
|
|
|
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
|
|
|
const IS_WINDOWS = process.platform === 'win32';
|
|
|
|
|
/*
|
|
|
|
|
* Class for running command line tools. Handles quoting and arg parsing in a platform agnostic way.
|
|
|
|
|
*/
|
|
|
|
|
class ToolRunner extends events.EventEmitter {
|
|
|
|
|
constructor(toolPath, args, options) {
|
|
|
|
|
super();
|
|
|
|
|
if (!toolPath) {
|
|
|
|
|
throw new Error("Parameter 'toolPath' cannot be null or empty.");
|
|
|
|
|
}
|
|
|
|
|
this.toolPath = toolPath;
|
|
|
|
|
this.args = args || [];
|
|
|
|
|
this.options = options || {};
|
|
|
|
|
}
|
|
|
|
|
_debug(message) {
|
|
|
|
|
if (this.options.listeners && this.options.listeners.debug) {
|
|
|
|
|
this.options.listeners.debug(message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_getCommandString(options, noPrefix) {
|
|
|
|
|
const toolPath = this._getSpawnFileName();
|
|
|
|
|
const args = this._getSpawnArgs(options);
|
|
|
|
|
let cmd = noPrefix ? '' : '[command]'; // omit prefix when piped to a second tool
|
|
|
|
|
if (IS_WINDOWS) {
|
|
|
|
|
// Windows + cmd file
|
|
|
|
|
if (this._isCmdFile()) {
|
|
|
|
|
cmd += toolPath;
|
|
|
|
|
for (const a of args) {
|
|
|
|
|
cmd += ` ${a}`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Windows + verbatim
|
|
|
|
|
else if (options.windowsVerbatimArguments) {
|
|
|
|
|
cmd += `"${toolPath}"`;
|
|
|
|
|
for (const a of args) {
|
|
|
|
|
cmd += ` ${a}`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Windows (regular)
|
|
|
|
|
else {
|
|
|
|
|
cmd += this._windowsQuoteCmdArg(toolPath);
|
|
|
|
|
for (const a of args) {
|
|
|
|
|
cmd += ` ${this._windowsQuoteCmdArg(a)}`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// OSX/Linux - this can likely be improved with some form of quoting.
|
|
|
|
|
// creating processes on Unix is fundamentally different than Windows.
|
|
|
|
|
// on Unix, execvp() takes an arg array.
|
|
|
|
|
cmd += toolPath;
|
|
|
|
|
for (const a of args) {
|
|
|
|
|
cmd += ` ${a}`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return cmd;
|
|
|
|
|
}
|
|
|
|
|
_processLineBuffer(data, strBuffer, onLine) {
|
|
|
|
|
try {
|
|
|
|
|
let s = strBuffer + data.toString();
|
|
|
|
|
let n = s.indexOf(os.EOL);
|
|
|
|
|
while (n > -1) {
|
|
|
|
|
const line = s.substring(0, n);
|
|
|
|
|
onLine(line);
|
|
|
|
|
// the rest of the string ...
|
|
|
|
|
s = s.substring(n + os.EOL.length);
|
|
|
|
|
n = s.indexOf(os.EOL);
|
|
|
|
|
}
|
|
|
|
|
strBuffer = s;
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
|
|
|
|
// streaming lines to console is best effort. Don't fail a build.
|
|
|
|
|
this._debug(`error processing line. Failed with error ${err}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_getSpawnFileName() {
|
|
|
|
|
if (IS_WINDOWS) {
|
|
|
|
|
if (this._isCmdFile()) {
|
|
|
|
|
return process.env['COMSPEC'] || 'cmd.exe';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return this.toolPath;
|
|
|
|
|
}
|
|
|
|
|
_getSpawnArgs(options) {
|
|
|
|
|
if (IS_WINDOWS) {
|
|
|
|
|
if (this._isCmdFile()) {
|
|
|
|
|
let argline = `/D /S /C "${this._windowsQuoteCmdArg(this.toolPath)}`;
|
|
|
|
|
for (const a of this.args) {
|
|
|
|
|
argline += ' ';
|
|
|
|
|
argline += options.windowsVerbatimArguments
|
|
|
|
|
? a
|
|
|
|
|
: this._windowsQuoteCmdArg(a);
|
|
|
|
|
}
|
|
|
|
|
argline += '"';
|
|
|
|
|
return [argline];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return this.args;
|
|
|
|
|
}
|
|
|
|
|
_endsWith(str, end) {
|
|
|
|
|
return str.endsWith(end);
|
|
|
|
|
}
|
|
|
|
|
_isCmdFile() {
|
|
|
|
|
const upperToolPath = this.toolPath.toUpperCase();
|
|
|
|
|
return (this._endsWith(upperToolPath, '.CMD') ||
|
|
|
|
|
this._endsWith(upperToolPath, '.BAT'));
|
|
|
|
|
}
|
|
|
|
|
_windowsQuoteCmdArg(arg) {
|
|
|
|
|
// for .exe, apply the normal quoting rules that libuv applies
|
|
|
|
|
if (!this._isCmdFile()) {
|
|
|
|
|
return this._uvQuoteCmdArg(arg);
|
|
|
|
|
}
|
|
|
|
|
// otherwise apply quoting rules specific to the cmd.exe command line parser.
|
|
|
|
|
// the libuv rules are generic and are not designed specifically for cmd.exe
|
|
|
|
|
// command line parser.
|
|
|
|
|
//
|
|
|
|
|
// for a detailed description of the cmd.exe command line parser, refer to
|
|
|
|
|
// http://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/7970912#7970912
|
|
|
|
|
// need quotes for empty arg
|
|
|
|
|
if (!arg) {
|
|
|
|
|
return '""';
|
|
|
|
|
}
|
|
|
|
|
// determine whether the arg needs to be quoted
|
|
|
|
|
const cmdSpecialChars = [
|
|
|
|
|
' ',
|
|
|
|
|
'\t',
|
|
|
|
|
'&',
|
|
|
|
|
'(',
|
|
|
|
|
')',
|
|
|
|
|
'[',
|
|
|
|
|
']',
|
|
|
|
|
'{',
|
|
|
|
|
'}',
|
|
|
|
|
'^',
|
|
|
|
|
'=',
|
|
|
|
|
';',
|
|
|
|
|
'!',
|
|
|
|
|
"'",
|
|
|
|
|
'+',
|
|
|
|
|
',',
|
|
|
|
|
'`',
|
|
|
|
|
'~',
|
|
|
|
|
'|',
|
|
|
|
|
'<',
|
|
|
|
|
'>',
|
|
|
|
|
'"'
|
|
|
|
|
];
|
|
|
|
|
let needsQuotes = false;
|
|
|
|
|
for (const char of arg) {
|
|
|
|
|
if (cmdSpecialChars.some(x => x === char)) {
|
|
|
|
|
needsQuotes = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// short-circuit if quotes not needed
|
|
|
|
|
if (!needsQuotes) {
|
|
|
|
|
return arg;
|
|
|
|
|
}
|
|
|
|
|
// the following quoting rules are very similar to the rules that by libuv applies.
|
|
|
|
|
//
|
|
|
|
|
// 1) wrap the string in quotes
|
|
|
|
|
//
|
|
|
|
|
// 2) double-up quotes - i.e. " => ""
|
|
|
|
|
//
|
|
|
|
|
// this is different from the libuv quoting rules. libuv replaces " with \", which unfortunately
|
|
|
|
|
// doesn't work well with a cmd.exe command line.
|
|
|
|
|
//
|
|
|
|
|
// note, replacing " with "" also works well if the arg is passed to a downstream .NET console app.
|
|
|
|
|
// for example, the command line:
|
|
|
|
|
// foo.exe "myarg:""my val"""
|
|
|
|
|
// is parsed by a .NET console app into an arg array:
|
|
|
|
|
// [ "myarg:\"my val\"" ]
|
|
|
|
|
// which is the same end result when applying libuv quoting rules. although the actual
|
|
|
|
|
// command line from libuv quoting rules would look like:
|
|
|
|
|
// foo.exe "myarg:\"my val\""
|
|
|
|
|
//
|
|
|
|
|
// 3) double-up slashes that precede a quote,
|
|
|
|
|
// e.g. hello \world => "hello \world"
|
|
|
|
|
// hello\"world => "hello\\""world"
|
|
|
|
|
// hello\\"world => "hello\\\\""world"
|
|
|
|
|
// hello world\ => "hello world\\"
|
|
|
|
|
//
|
|
|
|
|
// technically this is not required for a cmd.exe command line, or the batch argument parser.
|
|
|
|
|
// the reasons for including this as a .cmd quoting rule are:
|
|
|
|
|
//
|
|
|
|
|
// a) this is optimized for the scenario where the argument is passed from the .cmd file to an
|
|
|
|
|
// external program. many programs (e.g. .NET console apps) rely on the slash-doubling rule.
|
|
|
|
|
//
|
|
|
|
|
// b) it's what we've been doing previously (by deferring to node default behavior) and we
|
|
|
|
|
// haven't heard any complaints about that aspect.
|
|
|
|
|
//
|
|
|
|
|
// note, a weakness of the quoting rules chosen here, is that % is not escaped. in fact, % cannot be
|
|
|
|
|
// escaped when used on the command line directly - even though within a .cmd file % can be escaped
|
|
|
|
|
// by using %%.
|
|
|
|
|
//
|
|
|
|
|
// the saving grace is, on the command line, %var% is left as-is if var is not defined. this contrasts
|
|
|
|
|
// the line parsing rules within a .cmd file, where if var is not defined it is replaced with nothing.
|
|
|
|
|
//
|
|
|
|
|
// one option that was explored was replacing % with ^% - i.e. %var% => ^%var^%. this hack would
|
|
|
|
|
// often work, since it is unlikely that var^ would exist, and the ^ character is removed when the
|
|
|
|
|
// variable is used. the problem, however, is that ^ is not removed when %* is used to pass the args
|
|
|
|
|
// to an external program.
|
|
|
|
|
//
|
|
|
|
|
// an unexplored potential solution for the % escaping problem, is to create a wrapper .cmd file.
|
|
|
|
|
// % can be escaped within a .cmd file.
|
|
|
|
|
let reverse = '"';
|
|
|
|
|
let quoteHit = true;
|
|
|
|
|
for (let i = arg.length; i > 0; i--) {
|
|
|
|
|
// walk the string in reverse
|
|
|
|
|
reverse += arg[i - 1];
|
|
|
|
|
if (quoteHit && arg[i - 1] === '\\') {
|
|
|
|
|
reverse += '\\'; // double the slash
|
|
|
|
|
}
|
|
|
|
|
else if (arg[i - 1] === '"') {
|
|
|
|
|
quoteHit = true;
|
|
|
|
|
reverse += '"'; // double the quote
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
quoteHit = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
reverse += '"';
|
|
|
|
|
return reverse
|
|
|
|
|
.split('')
|
|
|
|
|
.reverse()
|
|
|
|
|
.join('');
|
|
|
|
|
}
|
|
|
|
|
_uvQuoteCmdArg(arg) {
|
|
|
|
|
// Tool runner wraps child_process.spawn() and needs to apply the same quoting as
|
|
|
|
|
// Node in certain cases where the undocumented spawn option windowsVerbatimArguments
|
|
|
|
|
// is used.
|
|
|
|
|
//
|
|
|
|
|
// Since this function is a port of quote_cmd_arg from Node 4.x (technically, lib UV,
|
|
|
|
|
// see https://github.com/nodejs/node/blob/v4.x/deps/uv/src/win/process.c for details),
|
|
|
|
|
// pasting copyright notice from Node within this function:
|
|
|
|
|
//
|
|
|
|
|
// Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
|
// of this software and associated documentation files (the "Software"), to
|
|
|
|
|
// deal in the Software without restriction, including without limitation the
|
|
|
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
|
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
|
//
|
|
|
|
|
// The above copyright notice and this permission notice shall be included in
|
|
|
|
|
// all copies or substantial portions of the Software.
|
|
|
|
|
//
|
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
|
// IN THE SOFTWARE.
|
|
|
|
|
if (!arg) {
|
|
|
|
|
// Need double quotation for empty argument
|
|
|
|
|
return '""';
|
|
|
|
|
}
|
|
|
|
|
if (!arg.includes(' ') && !arg.includes('\t') && !arg.includes('"')) {
|
|
|
|
|
// No quotation needed
|
|
|
|
|
return arg;
|
|
|
|
|
}
|
|
|
|
|
if (!arg.includes('"') && !arg.includes('\\')) {
|
|
|
|
|
// No embedded double quotes or backslashes, so I can just wrap
|
|
|
|
|
// quote marks around the whole thing.
|
|
|
|
|
return `"${arg}"`;
|
|
|
|
|
}
|
|
|
|
|
// Expected input/output:
|
|
|
|
|
// input : hello"world
|
|
|
|
|
// output: "hello\"world"
|
|
|
|
|
// input : hello""world
|
|
|
|
|
// output: "hello\"\"world"
|
|
|
|
|
// input : hello\world
|
|
|
|
|
// output: hello\world
|
|
|
|
|
// input : hello\\world
|
|
|
|
|
// output: hello\\world
|
|
|
|
|
// input : hello\"world
|
|
|
|
|
// output: "hello\\\"world"
|
|
|
|
|
// input : hello\\"world
|
|
|
|
|
// output: "hello\\\\\"world"
|
|
|
|
|
// input : hello world\
|
|
|
|
|
// output: "hello world\\" - note the comment in libuv actually reads "hello world\"
|
|
|
|
|
// but it appears the comment is wrong, it should be "hello world\\"
|
|
|
|
|
let reverse = '"';
|
|
|
|
|
let quoteHit = true;
|
|
|
|
|
for (let i = arg.length; i > 0; i--) {
|
|
|
|
|
// walk the string in reverse
|
|
|
|
|
reverse += arg[i - 1];
|
|
|
|
|
if (quoteHit && arg[i - 1] === '\\') {
|
|
|
|
|
reverse += '\\';
|
|
|
|
|
}
|
|
|
|
|
else if (arg[i - 1] === '"') {
|
|
|
|
|
quoteHit = true;
|
|
|
|
|
reverse += '\\';
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
quoteHit = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
reverse += '"';
|
|
|
|
|
return reverse
|
|
|
|
|
.split('')
|
|
|
|
|
.reverse()
|
|
|
|
|
.join('');
|
|
|
|
|
}
|
|
|
|
|
_cloneExecOptions(options) {
|
|
|
|
|
options = options || {};
|
|
|
|
|
const result = {
|
|
|
|
|
cwd: options.cwd || process.cwd(),
|
|
|
|
|
env: options.env || process.env,
|
|
|
|
|
silent: options.silent || false,
|
|
|
|
|
windowsVerbatimArguments: options.windowsVerbatimArguments || false,
|
|
|
|
|
failOnStdErr: options.failOnStdErr || false,
|
|
|
|
|
ignoreReturnCode: options.ignoreReturnCode || false,
|
|
|
|
|
delay: options.delay || 10000
|
|
|
|
|
};
|
|
|
|
|
result.outStream = options.outStream || process.stdout;
|
|
|
|
|
result.errStream = options.errStream || process.stderr;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
_getSpawnOptions(options, toolPath) {
|
|
|
|
|
options = options || {};
|
|
|
|
|
const result = {};
|
|
|
|
|
result.cwd = options.cwd;
|
|
|
|
|
result.env = options.env;
|
|
|
|
|
result['windowsVerbatimArguments'] =
|
|
|
|
|
options.windowsVerbatimArguments || this._isCmdFile();
|
|
|
|
|
if (options.windowsVerbatimArguments) {
|
|
|
|
|
result.argv0 = `"${toolPath}"`;
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* Exec a tool.
|
|
|
|
|
* Output will be streamed to the live console.
|
|
|
|
|
* Returns promise with return code
|
|
|
|
|
*
|
|
|
|
|
* @param tool path to tool to exec
|
|
|
|
|
* @param options optional exec options. See ExecOptions
|
|
|
|
|
* @returns number
|
|
|
|
|
*/
|
|
|
|
|
exec() {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
this._debug(`exec tool: ${this.toolPath}`);
|
|
|
|
|
this._debug('arguments:');
|
|
|
|
|
for (const arg of this.args) {
|
|
|
|
|
this._debug(` ${arg}`);
|
|
|
|
|
}
|
|
|
|
|
const optionsNonNull = this._cloneExecOptions(this.options);
|
|
|
|
|
if (!optionsNonNull.silent && optionsNonNull.outStream) {
|
|
|
|
|
optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);
|
|
|
|
|
}
|
|
|
|
|
const state = new ExecState(optionsNonNull, this.toolPath);
|
|
|
|
|
state.on('debug', (message) => {
|
|
|
|
|
this._debug(message);
|
|
|
|
|
});
|
|
|
|
|
const fileName = this._getSpawnFileName();
|
|
|
|
|
const cp = child.spawn(fileName, this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(this.options, fileName));
|
|
|
|
|
const stdbuffer = '';
|
|
|
|
|
if (cp.stdout) {
|
|
|
|
|
cp.stdout.on('data', (data) => {
|
|
|
|
|
if (this.options.listeners && this.options.listeners.stdout) {
|
|
|
|
|
this.options.listeners.stdout(data);
|
|
|
|
|
}
|
|
|
|
|
if (!optionsNonNull.silent && optionsNonNull.outStream) {
|
|
|
|
|
optionsNonNull.outStream.write(data);
|
|
|
|
|
}
|
|
|
|
|
this._processLineBuffer(data, stdbuffer, (line) => {
|
|
|
|
|
if (this.options.listeners && this.options.listeners.stdline) {
|
|
|
|
|
this.options.listeners.stdline(line);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
const errbuffer = '';
|
|
|
|
|
if (cp.stderr) {
|
|
|
|
|
cp.stderr.on('data', (data) => {
|
|
|
|
|
state.processStderr = true;
|
|
|
|
|
if (this.options.listeners && this.options.listeners.stderr) {
|
|
|
|
|
this.options.listeners.stderr(data);
|
|
|
|
|
}
|
|
|
|
|
if (!optionsNonNull.silent &&
|
|
|
|
|
optionsNonNull.errStream &&
|
|
|
|
|
optionsNonNull.outStream) {
|
|
|
|
|
const s = optionsNonNull.failOnStdErr
|
|
|
|
|
? optionsNonNull.errStream
|
|
|
|
|
: optionsNonNull.outStream;
|
|
|
|
|
s.write(data);
|
|
|
|
|
}
|
|
|
|
|
this._processLineBuffer(data, errbuffer, (line) => {
|
|
|
|
|
if (this.options.listeners && this.options.listeners.errline) {
|
|
|
|
|
this.options.listeners.errline(line);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
cp.on('error', (err) => {
|
|
|
|
|
state.processError = err.message;
|
|
|
|
|
state.processExited = true;
|
|
|
|
|
state.processClosed = true;
|
|
|
|
|
state.CheckComplete();
|
|
|
|
|
});
|
|
|
|
|
cp.on('exit', (code) => {
|
|
|
|
|
state.processExitCode = code;
|
|
|
|
|
state.processExited = true;
|
|
|
|
|
this._debug(`Exit code ${code} received from tool '${this.toolPath}'`);
|
|
|
|
|
state.CheckComplete();
|
|
|
|
|
});
|
|
|
|
|
cp.on('close', (code) => {
|
|
|
|
|
state.processExitCode = code;
|
|
|
|
|
state.processExited = true;
|
|
|
|
|
state.processClosed = true;
|
|
|
|
|
this._debug(`STDIO streams have closed for tool '${this.toolPath}'`);
|
|
|
|
|
state.CheckComplete();
|
|
|
|
|
});
|
|
|
|
|
state.on('done', (error, exitCode) => {
|
|
|
|
|
if (stdbuffer.length > 0) {
|
|
|
|
|
this.emit('stdline', stdbuffer);
|
|
|
|
|
}
|
|
|
|
|
if (errbuffer.length > 0) {
|
|
|
|
|
this.emit('errline', errbuffer);
|
|
|
|
|
}
|
|
|
|
|
cp.removeAllListeners();
|
|
|
|
|
if (error) {
|
|
|
|
|
reject(error);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
resolve(exitCode);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
exports.ToolRunner = ToolRunner;
|
|
|
|
|
/**
|
|
|
|
|
* Convert an arg string to an array of args. Handles escaping
|
|
|
|
|
*
|
|
|
|
|
* @param argString string of arguments
|
|
|
|
|
* @returns string[] array of arguments
|
|
|
|
|
*/
|
|
|
|
|
function argStringToArray(argString) {
|
|
|
|
|
const args = [];
|
|
|
|
|
let inQuotes = false;
|
|
|
|
|
let escaped = false;
|
|
|
|
|
let arg = '';
|
|
|
|
|
function append(c) {
|
|
|
|
|
// we only escape double quotes.
|
|
|
|
|
if (escaped && c !== '"') {
|
|
|
|
|
arg += '\\';
|
|
|
|
|
}
|
|
|
|
|
arg += c;
|
|
|
|
|
escaped = false;
|
|
|
|
|
}
|
|
|
|
|
for (let i = 0; i < argString.length; i++) {
|
|
|
|
|
const c = argString.charAt(i);
|
|
|
|
|
if (c === '"') {
|
|
|
|
|
if (!escaped) {
|
|
|
|
|
inQuotes = !inQuotes;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
append(c);
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (c === '\\' && escaped) {
|
|
|
|
|
append(c);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (c === '\\' && inQuotes) {
|
|
|
|
|
escaped = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (c === ' ' && !inQuotes) {
|
|
|
|
|
if (arg.length > 0) {
|
|
|
|
|
args.push(arg);
|
|
|
|
|
arg = '';
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
append(c);
|
|
|
|
|
}
|
|
|
|
|
if (arg.length > 0) {
|
|
|
|
|
args.push(arg.trim());
|
|
|
|
|
}
|
|
|
|
|
return args;
|
|
|
|
|
}
|
|
|
|
|
exports.argStringToArray = argStringToArray;
|
|
|
|
|
class ExecState extends events.EventEmitter {
|
|
|
|
|
constructor(options, toolPath) {
|
|
|
|
|
super();
|
|
|
|
|
this.processClosed = false; // tracks whether the process has exited and stdio is closed
|
|
|
|
|
this.processError = '';
|
|
|
|
|
this.processExitCode = 0;
|
|
|
|
|
this.processExited = false; // tracks whether the process has exited
|
|
|
|
|
this.processStderr = false; // tracks whether stderr was written to
|
|
|
|
|
this.delay = 10000; // 10 seconds
|
|
|
|
|
this.done = false;
|
|
|
|
|
this.timeout = null;
|
|
|
|
|
if (!toolPath) {
|
|
|
|
|
throw new Error('toolPath must not be empty');
|
|
|
|
|
}
|
|
|
|
|
this.options = options;
|
|
|
|
|
this.toolPath = toolPath;
|
|
|
|
|
if (options.delay) {
|
|
|
|
|
this.delay = options.delay;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CheckComplete() {
|
|
|
|
|
if (this.done) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (this.processClosed) {
|
|
|
|
|
this._setResult();
|
|
|
|
|
}
|
|
|
|
|
else if (this.processExited) {
|
|
|
|
|
this.timeout = setTimeout(ExecState.HandleTimeout, this.delay, this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_debug(message) {
|
|
|
|
|
this.emit('debug', message);
|
|
|
|
|
}
|
|
|
|
|
_setResult() {
|
|
|
|
|
// determine whether there is an error
|
|
|
|
|
let error;
|
|
|
|
|
if (this.processExited) {
|
|
|
|
|
if (this.processError) {
|
|
|
|
|
error = new Error(`There was an error when attempting to execute the process '${this.toolPath}'. This may indicate the process failed to start. Error: ${this.processError}`);
|
|
|
|
|
}
|
|
|
|
|
else if (this.processExitCode !== 0 && !this.options.ignoreReturnCode) {
|
|
|
|
|
error = new Error(`The process '${this.toolPath}' failed with exit code ${this.processExitCode}`);
|
|
|
|
|
}
|
|
|
|
|
else if (this.processStderr && this.options.failOnStdErr) {
|
|
|
|
|
error = new Error(`The process '${this.toolPath}' failed because one or more lines were written to the STDERR stream`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// clear the timeout
|
|
|
|
|
if (this.timeout) {
|
|
|
|
|
clearTimeout(this.timeout);
|
|
|
|
|
this.timeout = null;
|
|
|
|
|
}
|
|
|
|
|
this.done = true;
|
|
|
|
|
this.emit('done', error, this.processExitCode);
|
|
|
|
|
}
|
|
|
|
|
static HandleTimeout(state) {
|
|
|
|
|
if (state.done) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!state.processClosed && state.processExited) {
|
|
|
|
|
const message = `The STDIO streams did not close within ${state.delay /
|
|
|
|
|
1000} seconds of the exit event from process '${state.toolPath}'. This may indicate a child process inherited the STDIO streams and has not yet exited.`;
|
|
|
|
|
state._debug(message);
|
|
|
|
|
}
|
|
|
|
|
state._setResult();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//# sourceMappingURL=toolrunner.js.map
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 12:
|
|
|
|
|
/***/ (function(__unusedmodule, exports) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
class BasicCredentialHandler {
|
|
|
|
|
constructor(username, password) {
|
|
|
|
|
this.username = username;
|
|
|
|
|
this.password = password;
|
|
|
|
|
}
|
|
|
|
|
// currently implements pre-authorization
|
|
|
|
|
// TODO: support preAuth = false where it hooks on 401
|
|
|
|
|
prepareRequest(options) {
|
|
|
|
|
options.headers['Authorization'] = 'Basic ' + new Buffer(this.username + ':' + this.password).toString('base64');
|
|
|
|
|
options.headers['X-TFS-FedAuthRedirect'] = 'Suppress';
|
|
|
|
|
}
|
|
|
|
|
// This handler cannot handle 401
|
|
|
|
|
canHandleAuthentication(response) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
handleAuthentication(httpClient, requestInfo, objs) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
exports.BasicCredentialHandler = BasicCredentialHandler;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 16:
|
|
|
|
|
/***/ (function(module) {
|
|
|
|
|
|
|
|
|
|
module.exports = require("tls");
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 87:
|
|
|
|
|
/***/ (function(module) {
|
|
|
|
|
|
|
|
|
|
module.exports = require("os");
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 105:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
|
|
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
|
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
|
|
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
|
|
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
|
|
|
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
|
|
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
const httpm = __webpack_require__(874);
|
|
|
|
|
const util = __webpack_require__(729);
|
|
|
|
|
class RestClient {
|
|
|
|
|
/**
|
|
|
|
|
* Creates an instance of the RestClient
|
|
|
|
|
* @constructor
|
|
|
|
|
* @param {string} userAgent - userAgent for requests
|
|
|
|
|
* @param {string} baseUrl - (Optional) If not specified, use full urls per request. If supplied and a function passes a relative url, it will be appended to this
|
|
|
|
|
* @param {ifm.IRequestHandler[]} handlers - handlers are typically auth handlers (basic, bearer, ntlm supplied)
|
|
|
|
|
* @param {ifm.IRequestOptions} requestOptions - options for each http requests (http proxy setting, socket timeout)
|
|
|
|
|
*/
|
|
|
|
|
constructor(userAgent, baseUrl, handlers, requestOptions) {
|
|
|
|
|
this.client = new httpm.HttpClient(userAgent, handlers, requestOptions);
|
|
|
|
|
if (baseUrl) {
|
|
|
|
|
this._baseUrl = baseUrl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* Gets a resource from an endpoint
|
|
|
|
|
* Be aware that not found returns a null. Other error conditions reject the promise
|
|
|
|
|
* @param {string} requestUrl - fully qualified or relative url
|
|
|
|
|
* @param {IRequestOptions} requestOptions - (optional) requestOptions object
|
|
|
|
|
*/
|
|
|
|
|
options(requestUrl, options) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
let url = util.getUrl(requestUrl, this._baseUrl);
|
|
|
|
|
let res = yield this.client.options(url, this._headersFromOptions(options));
|
|
|
|
|
return this._processResponse(res, options);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* Gets a resource from an endpoint
|
|
|
|
|
* Be aware that not found returns a null. Other error conditions reject the promise
|
|
|
|
|
* @param {string} resource - fully qualified url or relative path
|
|
|
|
|
* @param {IRequestOptions} requestOptions - (optional) requestOptions object
|
|
|
|
|
*/
|
|
|
|
|
get(resource, options) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
let url = util.getUrl(resource, this._baseUrl);
|
|
|
|
|
let res = yield this.client.get(url, this._headersFromOptions(options));
|
|
|
|
|
return this._processResponse(res, options);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* Deletes a resource from an endpoint
|
|
|
|
|
* Be aware that not found returns a null. Other error conditions reject the promise
|
|
|
|
|
* @param {string} resource - fully qualified or relative url
|
|
|
|
|
* @param {IRequestOptions} requestOptions - (optional) requestOptions object
|
|
|
|
|
*/
|
|
|
|
|
del(resource, options) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
let url = util.getUrl(resource, this._baseUrl);
|
|
|
|
|
let res = yield this.client.del(url, this._headersFromOptions(options));
|
|
|
|
|
return this._processResponse(res, options);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* Creates resource(s) from an endpoint
|
|
|
|
|
* T type of object returned.
|
|
|
|
|
* Be aware that not found returns a null. Other error conditions reject the promise
|
|
|
|
|
* @param {string} resource - fully qualified or relative url
|
|
|
|
|
* @param {IRequestOptions} requestOptions - (optional) requestOptions object
|
|
|
|
|
*/
|
|
|
|
|
create(resource, resources, options) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
let url = util.getUrl(resource, this._baseUrl);
|
|
|
|
|
let headers = this._headersFromOptions(options, true);
|
|
|
|
|
let data = JSON.stringify(resources, null, 2);
|
|
|
|
|
let res = yield this.client.post(url, data, headers);
|
|
|
|
|
return this._processResponse(res, options);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* Updates resource(s) from an endpoint
|
|
|
|
|
* T type of object returned.
|
|
|
|
|
* Be aware that not found returns a null. Other error conditions reject the promise
|
|
|
|
|
* @param {string} resource - fully qualified or relative url
|
|
|
|
|
* @param {IRequestOptions} requestOptions - (optional) requestOptions object
|
|
|
|
|
*/
|
|
|
|
|
update(resource, resources, options) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
let url = util.getUrl(resource, this._baseUrl);
|
|
|
|
|
let headers = this._headersFromOptions(options, true);
|
|
|
|
|
let data = JSON.stringify(resources, null, 2);
|
|
|
|
|
let res = yield this.client.patch(url, data, headers);
|
|
|
|
|
return this._processResponse(res, options);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* Replaces resource(s) from an endpoint
|
|
|
|
|
* T type of object returned.
|
|
|
|
|
* Be aware that not found returns a null. Other error conditions reject the promise
|
|
|
|
|
* @param {string} resource - fully qualified or relative url
|
|
|
|
|
* @param {IRequestOptions} requestOptions - (optional) requestOptions object
|
|
|
|
|
*/
|
|
|
|
|
replace(resource, resources, options) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
let url = util.getUrl(resource, this._baseUrl);
|
|
|
|
|
let headers = this._headersFromOptions(options, true);
|
|
|
|
|
let data = JSON.stringify(resources, null, 2);
|
|
|
|
|
let res = yield this.client.put(url, data, headers);
|
|
|
|
|
return this._processResponse(res, options);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
uploadStream(verb, requestUrl, stream, options) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
let url = util.getUrl(requestUrl, this._baseUrl);
|
|
|
|
|
let headers = this._headersFromOptions(options, true);
|
|
|
|
|
let res = yield this.client.sendStream(verb, url, stream, headers);
|
|
|
|
|
return this._processResponse(res, options);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
_headersFromOptions(options, contentType) {
|
|
|
|
|
options = options || {};
|
|
|
|
|
let headers = options.additionalHeaders || {};
|
|
|
|
|
headers["Accept"] = options.acceptHeader || "application/json";
|
|
|
|
|
if (contentType) {
|
|
|
|
|
let found = false;
|
|
|
|
|
for (let header in headers) {
|
|
|
|
|
if (header.toLowerCase() == "content-type") {
|
|
|
|
|
found = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!found) {
|
|
|
|
|
headers["Content-Type"] = 'application/json; charset=utf-8';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return headers;
|
|
|
|
|
}
|
|
|
|
|
static dateTimeDeserializer(key, value) {
|
|
|
|
|
if (typeof value === 'string') {
|
|
|
|
|
let a = new Date(value);
|
|
|
|
|
if (!isNaN(a.valueOf())) {
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
_processResponse(res, options) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
const statusCode = res.message.statusCode;
|
|
|
|
|
const response = {
|
|
|
|
|
statusCode: statusCode,
|
|
|
|
|
result: null,
|
|
|
|
|
headers: {}
|
|
|
|
|
};
|
|
|
|
|
// not found leads to null obj returned
|
|
|
|
|
if (statusCode == httpm.HttpCodes.NotFound) {
|
|
|
|
|
resolve(response);
|
|
|
|
|
}
|
|
|
|
|
let obj;
|
|
|
|
|
let contents;
|
|
|
|
|
// get the result from the body
|
|
|
|
|
try {
|
|
|
|
|
contents = yield res.readBody();
|
|
|
|
|
if (contents && contents.length > 0) {
|
|
|
|
|
if (options && options.deserializeDates) {
|
|
|
|
|
obj = JSON.parse(contents, RestClient.dateTimeDeserializer);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
obj = JSON.parse(contents);
|
|
|
|
|
}
|
|
|
|
|
if (options && options.responseProcessor) {
|
|
|
|
|
response.result = options.responseProcessor(obj);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
response.result = obj;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
response.headers = res.message.headers;
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
|
|
|
|
// Invalid resource (contents not json); leaving result obj null
|
|
|
|
|
}
|
|
|
|
|
// note that 3xx redirects are handled by the http layer.
|
|
|
|
|
if (statusCode > 299) {
|
|
|
|
|
let msg;
|
|
|
|
|
// if exception/error in body, attempt to get better error
|
|
|
|
|
if (obj && obj.message) {
|
|
|
|
|
msg = obj.message;
|
|
|
|
|
}
|
|
|
|
|
else if (contents && contents.length > 0) {
|
|
|
|
|
// it may be the case that the exception is in the body message as string
|
|
|
|
|
msg = contents;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
msg = "Failed request: (" + statusCode + ")";
|
|
|
|
|
}
|
|
|
|
|
let err = new Error(msg);
|
|
|
|
|
// attach statusCode and body obj (if available) to the error object
|
|
|
|
|
err['statusCode'] = statusCode;
|
|
|
|
|
if (response.result) {
|
|
|
|
|
err['result'] = response.result;
|
|
|
|
|
}
|
|
|
|
|
reject(err);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
resolve(response);
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
exports.RestClient = RestClient;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 129:
|
|
|
|
|
/***/ (function(module) {
|
|
|
|
|
|
|
|
|
|
module.exports = require("child_process");
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 139:
|
|
|
|
|
/***/ (function(module, __unusedexports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
// Unique ID creation requires a high quality random # generator. In node.js
|
|
|
|
|
// this is pretty straight-forward - we use the crypto API.
|
|
|
|
|
|
|
|
|
|
var crypto = __webpack_require__(417);
|
|
|
|
|
|
|
|
|
|
module.exports = function nodeRNG() {
|
|
|
|
|
return crypto.randomBytes(16);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 141:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var net = __webpack_require__(631);
|
|
|
|
|
var tls = __webpack_require__(16);
|
|
|
|
|
var http = __webpack_require__(605);
|
|
|
|
|
var https = __webpack_require__(211);
|
|
|
|
|
var events = __webpack_require__(614);
|
|
|
|
|
var assert = __webpack_require__(357);
|
|
|
|
|
var util = __webpack_require__(669);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exports.httpOverHttp = httpOverHttp;
|
|
|
|
|
exports.httpsOverHttp = httpsOverHttp;
|
|
|
|
|
exports.httpOverHttps = httpOverHttps;
|
|
|
|
|
exports.httpsOverHttps = httpsOverHttps;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function httpOverHttp(options) {
|
|
|
|
|
var agent = new TunnelingAgent(options);
|
|
|
|
|
agent.request = http.request;
|
|
|
|
|
return agent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function httpsOverHttp(options) {
|
|
|
|
|
var agent = new TunnelingAgent(options);
|
|
|
|
|
agent.request = http.request;
|
|
|
|
|
agent.createSocket = createSecureSocket;
|
|
|
|
|
return agent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function httpOverHttps(options) {
|
|
|
|
|
var agent = new TunnelingAgent(options);
|
|
|
|
|
agent.request = https.request;
|
|
|
|
|
return agent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function httpsOverHttps(options) {
|
|
|
|
|
var agent = new TunnelingAgent(options);
|
|
|
|
|
agent.request = https.request;
|
|
|
|
|
agent.createSocket = createSecureSocket;
|
|
|
|
|
return agent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function TunnelingAgent(options) {
|
|
|
|
|
var self = this;
|
|
|
|
|
self.options = options || {};
|
|
|
|
|
self.proxyOptions = self.options.proxy || {};
|
|
|
|
|
self.maxSockets = self.options.maxSockets || http.Agent.defaultMaxSockets;
|
|
|
|
|
self.requests = [];
|
|
|
|
|
self.sockets = [];
|
|
|
|
|
|
|
|
|
|
self.on('free', function onFree(socket, host, port, localAddress) {
|
|
|
|
|
var options = toOptions(host, port, localAddress);
|
|
|
|
|
for (var i = 0, len = self.requests.length; i < len; ++i) {
|
|
|
|
|
var pending = self.requests[i];
|
|
|
|
|
if (pending.host === options.host && pending.port === options.port) {
|
|
|
|
|
// Detect the request to connect same origin server,
|
|
|
|
|
// reuse the connection.
|
|
|
|
|
self.requests.splice(i, 1);
|
|
|
|
|
pending.request.onSocket(socket);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
socket.destroy();
|
|
|
|
|
self.removeSocket(socket);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
util.inherits(TunnelingAgent, events.EventEmitter);
|
|
|
|
|
|
|
|
|
|
TunnelingAgent.prototype.addRequest = function addRequest(req, host, port, localAddress) {
|
|
|
|
|
var self = this;
|
|
|
|
|
var options = mergeOptions({request: req}, self.options, toOptions(host, port, localAddress));
|
|
|
|
|
|
|
|
|
|
if (self.sockets.length >= this.maxSockets) {
|
|
|
|
|
// We are over limit so we'll add it to the queue.
|
|
|
|
|
self.requests.push(options);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we are under maxSockets create a new one.
|
|
|
|
|
self.createSocket(options, function(socket) {
|
|
|
|
|
socket.on('free', onFree);
|
|
|
|
|
socket.on('close', onCloseOrRemove);
|
|
|
|
|
socket.on('agentRemove', onCloseOrRemove);
|
|
|
|
|
req.onSocket(socket);
|
|
|
|
|
|
|
|
|
|
function onFree() {
|
|
|
|
|
self.emit('free', socket, options);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onCloseOrRemove(err) {
|
|
|
|
|
self.removeSocket(socket);
|
|
|
|
|
socket.removeListener('free', onFree);
|
|
|
|
|
socket.removeListener('close', onCloseOrRemove);
|
|
|
|
|
socket.removeListener('agentRemove', onCloseOrRemove);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TunnelingAgent.prototype.createSocket = function createSocket(options, cb) {
|
|
|
|
|
var self = this;
|
|
|
|
|
var placeholder = {};
|
|
|
|
|
self.sockets.push(placeholder);
|
|
|
|
|
|
|
|
|
|
var connectOptions = mergeOptions({}, self.proxyOptions, {
|
|
|
|
|
method: 'CONNECT',
|
|
|
|
|
path: options.host + ':' + options.port,
|
|
|
|
|
agent: false
|
|
|
|
|
});
|
|
|
|
|
if (connectOptions.proxyAuth) {
|
|
|
|
|
connectOptions.headers = connectOptions.headers || {};
|
|
|
|
|
connectOptions.headers['Proxy-Authorization'] = 'Basic ' +
|
|
|
|
|
new Buffer(connectOptions.proxyAuth).toString('base64');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
debug('making CONNECT request');
|
|
|
|
|
var connectReq = self.request(connectOptions);
|
|
|
|
|
connectReq.useChunkedEncodingByDefault = false; // for v0.6
|
|
|
|
|
connectReq.once('response', onResponse); // for v0.6
|
|
|
|
|
connectReq.once('upgrade', onUpgrade); // for v0.6
|
|
|
|
|
connectReq.once('connect', onConnect); // for v0.7 or later
|
|
|
|
|
connectReq.once('error', onError);
|
|
|
|
|
connectReq.end();
|
|
|
|
|
|
|
|
|
|
function onResponse(res) {
|
|
|
|
|
// Very hacky. This is necessary to avoid http-parser leaks.
|
|
|
|
|
res.upgrade = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onUpgrade(res, socket, head) {
|
|
|
|
|
// Hacky.
|
|
|
|
|
process.nextTick(function() {
|
|
|
|
|
onConnect(res, socket, head);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onConnect(res, socket, head) {
|
|
|
|
|
connectReq.removeAllListeners();
|
|
|
|
|
socket.removeAllListeners();
|
|
|
|
|
|
|
|
|
|
if (res.statusCode === 200) {
|
|
|
|
|
assert.equal(head.length, 0);
|
|
|
|
|
debug('tunneling connection has established');
|
|
|
|
|
self.sockets[self.sockets.indexOf(placeholder)] = socket;
|
|
|
|
|
cb(socket);
|
|
|
|
|
} else {
|
|
|
|
|
debug('tunneling socket could not be established, statusCode=%d',
|
|
|
|
|
res.statusCode);
|
|
|
|
|
var error = new Error('tunneling socket could not be established, ' +
|
|
|
|
|
'statusCode=' + res.statusCode);
|
|
|
|
|
error.code = 'ECONNRESET';
|
|
|
|
|
options.request.emit('error', error);
|
|
|
|
|
self.removeSocket(placeholder);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onError(cause) {
|
|
|
|
|
connectReq.removeAllListeners();
|
|
|
|
|
|
|
|
|
|
debug('tunneling socket could not be established, cause=%s\n',
|
|
|
|
|
cause.message, cause.stack);
|
|
|
|
|
var error = new Error('tunneling socket could not be established, ' +
|
|
|
|
|
'cause=' + cause.message);
|
|
|
|
|
error.code = 'ECONNRESET';
|
|
|
|
|
options.request.emit('error', error);
|
|
|
|
|
self.removeSocket(placeholder);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TunnelingAgent.prototype.removeSocket = function removeSocket(socket) {
|
|
|
|
|
var pos = this.sockets.indexOf(socket)
|
|
|
|
|
if (pos === -1) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this.sockets.splice(pos, 1);
|
|
|
|
|
|
|
|
|
|
var pending = this.requests.shift();
|
|
|
|
|
if (pending) {
|
|
|
|
|
// If we have pending requests and a socket gets closed a new one
|
|
|
|
|
// needs to be created to take over in the pool for the one that closed.
|
|
|
|
|
this.createSocket(pending, function(socket) {
|
|
|
|
|
pending.request.onSocket(socket);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function createSecureSocket(options, cb) {
|
|
|
|
|
var self = this;
|
|
|
|
|
TunnelingAgent.prototype.createSocket.call(self, options, function(socket) {
|
|
|
|
|
var hostHeader = options.request.getHeader('host');
|
|
|
|
|
var tlsOptions = mergeOptions({}, self.options, {
|
|
|
|
|
socket: socket,
|
|
|
|
|
servername: hostHeader ? hostHeader.replace(/:.*$/, '') : options.host
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 0 is dummy port for v0.6
|
|
|
|
|
var secureSocket = tls.connect(0, tlsOptions);
|
|
|
|
|
self.sockets[self.sockets.indexOf(socket)] = secureSocket;
|
|
|
|
|
cb(secureSocket);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function toOptions(host, port, localAddress) {
|
|
|
|
|
if (typeof host === 'string') { // since v0.10
|
|
|
|
|
return {
|
|
|
|
|
host: host,
|
|
|
|
|
port: port,
|
|
|
|
|
localAddress: localAddress
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return host; // for v0.11 or later
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mergeOptions(target) {
|
|
|
|
|
for (var i = 1, len = arguments.length; i < len; ++i) {
|
|
|
|
|
var overrides = arguments[i];
|
|
|
|
|
if (typeof overrides === 'object') {
|
|
|
|
|
var keys = Object.keys(overrides);
|
|
|
|
|
for (var j = 0, keyLen = keys.length; j < keyLen; ++j) {
|
|
|
|
|
var k = keys[j];
|
|
|
|
|
if (overrides[k] !== undefined) {
|
|
|
|
|
target[k] = overrides[k];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var debug;
|
|
|
|
|
if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) {
|
|
|
|
|
debug = function() {
|
|
|
|
|
var args = Array.prototype.slice.call(arguments);
|
|
|
|
|
if (typeof args[0] === 'string') {
|
|
|
|
|
args[0] = 'TUNNEL: ' + args[0];
|
|
|
|
|
} else {
|
|
|
|
|
args.unshift('TUNNEL:');
|
|
|
|
|
}
|
|
|
|
|
console.error.apply(console, args);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
debug = function() {};
|
|
|
|
|
}
|
|
|
|
|
exports.debug = debug; // for test
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 154:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
|
|
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
|
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
|
|
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
|
|
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
|
|
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
|
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
|
|
if (mod && mod.__esModule) return mod;
|
|
|
|
|
var result = {};
|
|
|
|
|
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
|
|
|
result["default"] = mod;
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
const core = __importStar(__webpack_require__(470));
|
|
|
|
|
const fs = __importStar(__webpack_require__(747));
|
|
|
|
|
const Handlers_1 = __webpack_require__(941);
|
|
|
|
|
const HttpClient_1 = __webpack_require__(874);
|
|
|
|
|
const RestClient_1 = __webpack_require__(105);
|
|
|
|
|
function getCacheUrl() {
|
|
|
|
|
// Ideally we just use ACTIONS_CACHE_URL
|
|
|
|
|
const cacheUrl = (process.env["ACTIONS_CACHE_URL"] ||
|
|
|
|
|
process.env["ACTIONS_RUNTIME_URL"] ||
|
|
|
|
|
"").replace("pipelines", "artifactcache");
|
|
|
|
|
if (!cacheUrl) {
|
|
|
|
|
throw new Error("Cache Service Url not found, unable to restore cache.");
|
|
|
|
|
}
|
|
|
|
|
core.debug(`Cache Url: ${cacheUrl}`);
|
|
|
|
|
return cacheUrl;
|
|
|
|
|
}
|
|
|
|
|
function createAcceptHeader(type, apiVersion) {
|
|
|
|
|
return `${type};api-version=${apiVersion}`;
|
|
|
|
|
}
|
|
|
|
|
function getRequestOptions() {
|
|
|
|
|
const requestOptions = {
|
|
|
|
|
acceptHeader: createAcceptHeader("application/json", "5.2-preview.1")
|
|
|
|
|
};
|
|
|
|
|
return requestOptions;
|
|
|
|
|
}
|
|
|
|
|
function getCacheEntry(keys) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
const cacheUrl = getCacheUrl();
|
|
|
|
|
const token = process.env["ACTIONS_RUNTIME_TOKEN"] || "";
|
|
|
|
|
const bearerCredentialHandler = new Handlers_1.BearerCredentialHandler(token);
|
|
|
|
|
const resource = `_apis/artifactcache/cache?keys=${encodeURIComponent(keys.join(","))}`;
|
|
|
|
|
const restClient = new RestClient_1.RestClient("actions/cache", cacheUrl, [
|
|
|
|
|
bearerCredentialHandler
|
|
|
|
|
]);
|
|
|
|
|
const response = yield restClient.get(resource, getRequestOptions());
|
|
|
|
|
if (response.statusCode === 204) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
if (response.statusCode !== 200) {
|
|
|
|
|
throw new Error(`Cache service responded with ${response.statusCode}`);
|
|
|
|
|
}
|
|
|
|
|
const cacheResult = response.result;
|
|
|
|
|
if (!cacheResult || !cacheResult.archiveLocation) {
|
|
|
|
|
throw new Error("Cache not found.");
|
|
|
|
|
}
|
|
|
|
|
core.setSecret(cacheResult.archiveLocation);
|
|
|
|
|
core.debug(`Cache Result:`);
|
|
|
|
|
core.debug(JSON.stringify(cacheResult));
|
|
|
|
|
return cacheResult;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.getCacheEntry = getCacheEntry;
|
|
|
|
|
function pipeResponseToStream(response, stream) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
|
response.message.pipe(stream).on("close", () => {
|
|
|
|
|
resolve();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
function downloadCache(cacheEntry, archivePath) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
const stream = fs.createWriteStream(archivePath);
|
|
|
|
|
const httpClient = new HttpClient_1.HttpClient("actions/cache");
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
|
|
|
const downloadResponse = yield httpClient.get(cacheEntry.archiveLocation);
|
|
|
|
|
yield pipeResponseToStream(downloadResponse, stream);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.downloadCache = downloadCache;
|
|
|
|
|
function saveCache(key, archivePath) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
const stream = fs.createReadStream(archivePath);
|
|
|
|
|
const cacheUrl = getCacheUrl();
|
|
|
|
|
const token = process.env["ACTIONS_RUNTIME_TOKEN"] || "";
|
|
|
|
|
const bearerCredentialHandler = new Handlers_1.BearerCredentialHandler(token);
|
|
|
|
|
const resource = `_apis/artifactcache/cache/${encodeURIComponent(key)}`;
|
|
|
|
|
const postUrl = cacheUrl + resource;
|
|
|
|
|
const restClient = new RestClient_1.RestClient("actions/cache", undefined, [
|
|
|
|
|
bearerCredentialHandler
|
|
|
|
|
]);
|
|
|
|
|
const requestOptions = getRequestOptions();
|
|
|
|
|
requestOptions.additionalHeaders = {
|
|
|
|
|
"Content-Type": "application/octet-stream"
|
|
|
|
|
};
|
|
|
|
|
const response = yield restClient.uploadStream("POST", postUrl, stream, requestOptions);
|
|
|
|
|
if (response.statusCode !== 200) {
|
|
|
|
|
throw new Error(`Cache service responded with ${response.statusCode}`);
|
|
|
|
|
}
|
|
|
|
|
core.info("Cache saved successfully");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.saveCache = saveCache;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 211:
|
|
|
|
|
/***/ (function(module) {
|
|
|
|
|
|
|
|
|
|
module.exports = require("https");
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 327:
|
|
|
|
|
/***/ (function(__unusedmodule, exports) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
class PersonalAccessTokenCredentialHandler {
|
|
|
|
|
constructor(token) {
|
|
|
|
|
this.token = token;
|
|
|
|
|
}
|
|
|
|
|
// currently implements pre-authorization
|
|
|
|
|
// TODO: support preAuth = false where it hooks on 401
|
|
|
|
|
prepareRequest(options) {
|
|
|
|
|
options.headers['Authorization'] = 'Basic ' + new Buffer('PAT:' + this.token).toString('base64');
|
|
|
|
|
options.headers['X-TFS-FedAuthRedirect'] = 'Suppress';
|
|
|
|
|
}
|
|
|
|
|
// This handler cannot handle 401
|
|
|
|
|
canHandleAuthentication(response) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
handleAuthentication(httpClient, requestInfo, objs) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
exports.PersonalAccessTokenCredentialHandler = PersonalAccessTokenCredentialHandler;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 357:
|
|
|
|
|
/***/ (function(module) {
|
|
|
|
|
|
|
|
|
|
module.exports = require("assert");
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 413:
|
|
|
|
|
/***/ (function(module, __unusedexports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
module.exports = __webpack_require__(141);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 417:
|
|
|
|
|
/***/ (function(module) {
|
|
|
|
|
|
|
|
|
|
module.exports = require("crypto");
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 431:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
const os = __webpack_require__(87);
|
|
|
|
|
/**
|
|
|
|
|
* Commands
|
|
|
|
|
*
|
|
|
|
|
* Command Format:
|
|
|
|
|
* ##[name key=value;key=value]message
|
|
|
|
|
*
|
|
|
|
|
* Examples:
|
|
|
|
|
* ##[warning]This is the user warning message
|
|
|
|
|
* ##[set-secret name=mypassword]definitelyNotAPassword!
|
|
|
|
|
*/
|
|
|
|
|
function issueCommand(command, properties, message) {
|
|
|
|
|
const cmd = new Command(command, properties, message);
|
|
|
|
|
process.stdout.write(cmd.toString() + os.EOL);
|
|
|
|
|
}
|
|
|
|
|
exports.issueCommand = issueCommand;
|
|
|
|
|
function issue(name, message = '') {
|
|
|
|
|
issueCommand(name, {}, message);
|
|
|
|
|
}
|
|
|
|
|
exports.issue = issue;
|
|
|
|
|
const CMD_STRING = '::';
|
|
|
|
|
class Command {
|
|
|
|
|
constructor(command, properties, message) {
|
|
|
|
|
if (!command) {
|
|
|
|
|
command = 'missing.command';
|
|
|
|
|
}
|
|
|
|
|
this.command = command;
|
|
|
|
|
this.properties = properties;
|
|
|
|
|
this.message = message;
|
|
|
|
|
}
|
|
|
|
|
toString() {
|
|
|
|
|
let cmdStr = CMD_STRING + this.command;
|
|
|
|
|
if (this.properties && Object.keys(this.properties).length > 0) {
|
|
|
|
|
cmdStr += ' ';
|
|
|
|
|
for (const key in this.properties) {
|
|
|
|
|
if (this.properties.hasOwnProperty(key)) {
|
|
|
|
|
const val = this.properties[key];
|
|
|
|
|
if (val) {
|
|
|
|
|
// safely append the val - avoid blowing up when attempting to
|
|
|
|
|
// call .replace() if message is not a string for some reason
|
|
|
|
|
cmdStr += `${key}=${escape(`${val || ''}`)},`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
cmdStr += CMD_STRING;
|
|
|
|
|
// safely append the message - avoid blowing up when attempting to
|
|
|
|
|
// call .replace() if message is not a string for some reason
|
|
|
|
|
const message = `${this.message || ''}`;
|
|
|
|
|
cmdStr += escapeData(message);
|
|
|
|
|
return cmdStr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
function escapeData(s) {
|
|
|
|
|
return s.replace(/\r/g, '%0D').replace(/\n/g, '%0A');
|
|
|
|
|
}
|
|
|
|
|
function escape(s) {
|
|
|
|
|
return s
|
|
|
|
|
.replace(/\r/g, '%0D')
|
|
|
|
|
.replace(/\n/g, '%0A')
|
|
|
|
|
.replace(/]/g, '%5D')
|
|
|
|
|
.replace(/;/g, '%3B');
|
|
|
|
|
}
|
|
|
|
|
//# sourceMappingURL=command.js.map
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 432:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
var crypto = __webpack_require__(417);
|
|
|
|
|
|
|
|
|
|
var flags = {
|
|
|
|
|
NTLM_NegotiateUnicode : 0x00000001,
|
|
|
|
|
NTLM_NegotiateOEM : 0x00000002,
|
|
|
|
|
NTLM_RequestTarget : 0x00000004,
|
|
|
|
|
NTLM_Unknown9 : 0x00000008,
|
|
|
|
|
NTLM_NegotiateSign : 0x00000010,
|
|
|
|
|
NTLM_NegotiateSeal : 0x00000020,
|
|
|
|
|
NTLM_NegotiateDatagram : 0x00000040,
|
|
|
|
|
NTLM_NegotiateLanManagerKey : 0x00000080,
|
|
|
|
|
NTLM_Unknown8 : 0x00000100,
|
|
|
|
|
NTLM_NegotiateNTLM : 0x00000200,
|
|
|
|
|
NTLM_NegotiateNTOnly : 0x00000400,
|
|
|
|
|
NTLM_Anonymous : 0x00000800,
|
|
|
|
|
NTLM_NegotiateOemDomainSupplied : 0x00001000,
|
|
|
|
|
NTLM_NegotiateOemWorkstationSupplied : 0x00002000,
|
|
|
|
|
NTLM_Unknown6 : 0x00004000,
|
|
|
|
|
NTLM_NegotiateAlwaysSign : 0x00008000,
|
|
|
|
|
NTLM_TargetTypeDomain : 0x00010000,
|
|
|
|
|
NTLM_TargetTypeServer : 0x00020000,
|
|
|
|
|
NTLM_TargetTypeShare : 0x00040000,
|
|
|
|
|
NTLM_NegotiateExtendedSecurity : 0x00080000,
|
|
|
|
|
NTLM_NegotiateIdentify : 0x00100000,
|
|
|
|
|
NTLM_Unknown5 : 0x00200000,
|
|
|
|
|
NTLM_RequestNonNTSessionKey : 0x00400000,
|
|
|
|
|
NTLM_NegotiateTargetInfo : 0x00800000,
|
|
|
|
|
NTLM_Unknown4 : 0x01000000,
|
|
|
|
|
NTLM_NegotiateVersion : 0x02000000,
|
|
|
|
|
NTLM_Unknown3 : 0x04000000,
|
|
|
|
|
NTLM_Unknown2 : 0x08000000,
|
|
|
|
|
NTLM_Unknown1 : 0x10000000,
|
|
|
|
|
NTLM_Negotiate128 : 0x20000000,
|
|
|
|
|
NTLM_NegotiateKeyExchange : 0x40000000,
|
|
|
|
|
NTLM_Negotiate56 : 0x80000000
|
|
|
|
|
};
|
|
|
|
|
var typeflags = {
|
|
|
|
|
NTLM_TYPE1_FLAGS : flags.NTLM_NegotiateUnicode
|
|
|
|
|
+ flags.NTLM_NegotiateOEM
|
|
|
|
|
+ flags.NTLM_RequestTarget
|
|
|
|
|
+ flags.NTLM_NegotiateNTLM
|
|
|
|
|
+ flags.NTLM_NegotiateOemDomainSupplied
|
|
|
|
|
+ flags.NTLM_NegotiateOemWorkstationSupplied
|
|
|
|
|
+ flags.NTLM_NegotiateAlwaysSign
|
|
|
|
|
+ flags.NTLM_NegotiateExtendedSecurity
|
|
|
|
|
+ flags.NTLM_NegotiateVersion
|
|
|
|
|
+ flags.NTLM_Negotiate128
|
|
|
|
|
+ flags.NTLM_Negotiate56,
|
|
|
|
|
|
|
|
|
|
NTLM_TYPE2_FLAGS : flags.NTLM_NegotiateUnicode
|
|
|
|
|
+ flags.NTLM_RequestTarget
|
|
|
|
|
+ flags.NTLM_NegotiateNTLM
|
|
|
|
|
+ flags.NTLM_NegotiateAlwaysSign
|
|
|
|
|
+ flags.NTLM_NegotiateExtendedSecurity
|
|
|
|
|
+ flags.NTLM_NegotiateTargetInfo
|
|
|
|
|
+ flags.NTLM_NegotiateVersion
|
|
|
|
|
+ flags.NTLM_Negotiate128
|
|
|
|
|
+ flags.NTLM_Negotiate56
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function createType1Message(options){
|
|
|
|
|
var domain = escape(options.domain.toUpperCase());
|
|
|
|
|
var workstation = escape(options.workstation.toUpperCase());
|
|
|
|
|
var protocol = 'NTLMSSP\0';
|
|
|
|
|
|
|
|
|
|
var BODY_LENGTH = 40;
|
|
|
|
|
|
|
|
|
|
var type1flags = typeflags.NTLM_TYPE1_FLAGS;
|
|
|
|
|
if(!domain || domain === '')
|
|
|
|
|
type1flags = type1flags - flags.NTLM_NegotiateOemDomainSupplied;
|
|
|
|
|
|
|
|
|
|
var pos = 0;
|
|
|
|
|
var buf = new Buffer(BODY_LENGTH + domain.length + workstation.length);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
buf.write(protocol, pos, protocol.length); pos += protocol.length; // protocol
|
|
|
|
|
buf.writeUInt32LE(1, pos); pos += 4; // type 1
|
|
|
|
|
buf.writeUInt32LE(type1flags, pos); pos += 4; // TYPE1 flag
|
|
|
|
|
|
|
|
|
|
buf.writeUInt16LE(domain.length, pos); pos += 2; // domain length
|
|
|
|
|
buf.writeUInt16LE(domain.length, pos); pos += 2; // domain max length
|
|
|
|
|
buf.writeUInt32LE(BODY_LENGTH + workstation.length, pos); pos += 4; // domain buffer offset
|
|
|
|
|
|
|
|
|
|
buf.writeUInt16LE(workstation.length, pos); pos += 2; // workstation length
|
|
|
|
|
buf.writeUInt16LE(workstation.length, pos); pos += 2; // workstation max length
|
|
|
|
|
buf.writeUInt32LE(BODY_LENGTH, pos); pos += 4; // workstation buffer offset
|
|
|
|
|
|
|
|
|
|
buf.writeUInt8(5, pos); pos += 1; //ProductMajorVersion
|
|
|
|
|
buf.writeUInt8(1, pos); pos += 1; //ProductMinorVersion
|
|
|
|
|
buf.writeUInt16LE(2600, pos); pos += 2; //ProductBuild
|
|
|
|
|
|
|
|
|
|
buf.writeUInt8(0 , pos); pos += 1; //VersionReserved1
|
|
|
|
|
buf.writeUInt8(0 , pos); pos += 1; //VersionReserved2
|
|
|
|
|
buf.writeUInt8(0 , pos); pos += 1; //VersionReserved3
|
|
|
|
|
buf.writeUInt8(15, pos); pos += 1; //NTLMRevisionCurrent
|
|
|
|
|
|
|
|
|
|
buf.write(workstation, pos, workstation.length, 'ascii'); pos += workstation.length; // workstation string
|
|
|
|
|
buf.write(domain , pos, domain.length , 'ascii'); pos += domain.length;
|
|
|
|
|
|
|
|
|
|
return 'NTLM ' + buf.toString('base64');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function parseType2Message(rawmsg, callback){
|
|
|
|
|
var match = rawmsg.match(/NTLM (.+)?/);
|
|
|
|
|
if(!match || !match[1])
|
|
|
|
|
return callback(new Error("Couldn't find NTLM in the message type2 comming from the server"));
|
|
|
|
|
|
|
|
|
|
var buf = new Buffer(match[1], 'base64');
|
|
|
|
|
|
|
|
|
|
var msg = {};
|
|
|
|
|
|
|
|
|
|
msg.signature = buf.slice(0, 8);
|
|
|
|
|
msg.type = buf.readInt16LE(8);
|
|
|
|
|
|
|
|
|
|
if(msg.type != 2)
|
|
|
|
|
return callback(new Error("Server didn't return a type 2 message"));
|
|
|
|
|
|
|
|
|
|
msg.targetNameLen = buf.readInt16LE(12);
|
|
|
|
|
msg.targetNameMaxLen = buf.readInt16LE(14);
|
|
|
|
|
msg.targetNameOffset = buf.readInt32LE(16);
|
|
|
|
|
msg.targetName = buf.slice(msg.targetNameOffset, msg.targetNameOffset + msg.targetNameMaxLen);
|
|
|
|
|
|
|
|
|
|
msg.negotiateFlags = buf.readInt32LE(20);
|
|
|
|
|
msg.serverChallenge = buf.slice(24, 32);
|
|
|
|
|
msg.reserved = buf.slice(32, 40);
|
|
|
|
|
|
|
|
|
|
if(msg.negotiateFlags & flags.NTLM_NegotiateTargetInfo){
|
|
|
|
|
msg.targetInfoLen = buf.readInt16LE(40);
|
|
|
|
|
msg.targetInfoMaxLen = buf.readInt16LE(42);
|
|
|
|
|
msg.targetInfoOffset = buf.readInt32LE(44);
|
|
|
|
|
msg.targetInfo = buf.slice(msg.targetInfoOffset, msg.targetInfoOffset + msg.targetInfoLen);
|
|
|
|
|
}
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createType3Message(msg2, options){
|
|
|
|
|
var nonce = msg2.serverChallenge;
|
|
|
|
|
var username = options.username;
|
|
|
|
|
var password = options.password;
|
|
|
|
|
var negotiateFlags = msg2.negotiateFlags;
|
|
|
|
|
|
|
|
|
|
var isUnicode = negotiateFlags & flags.NTLM_NegotiateUnicode;
|
|
|
|
|
var isNegotiateExtendedSecurity = negotiateFlags & flags.NTLM_NegotiateExtendedSecurity;
|
|
|
|
|
|
|
|
|
|
var BODY_LENGTH = 72;
|
|
|
|
|
|
|
|
|
|
var domainName = escape(options.domain.toUpperCase());
|
|
|
|
|
var workstation = escape(options.workstation.toUpperCase());
|
|
|
|
|
|
|
|
|
|
var workstationBytes, domainNameBytes, usernameBytes, encryptedRandomSessionKeyBytes;
|
|
|
|
|
|
|
|
|
|
var encryptedRandomSessionKey = "";
|
|
|
|
|
if(isUnicode){
|
|
|
|
|
workstationBytes = new Buffer(workstation, 'utf16le');
|
|
|
|
|
domainNameBytes = new Buffer(domainName, 'utf16le');
|
|
|
|
|
usernameBytes = new Buffer(username, 'utf16le');
|
|
|
|
|
encryptedRandomSessionKeyBytes = new Buffer(encryptedRandomSessionKey, 'utf16le');
|
|
|
|
|
}else{
|
|
|
|
|
workstationBytes = new Buffer(workstation, 'ascii');
|
|
|
|
|
domainNameBytes = new Buffer(domainName, 'ascii');
|
|
|
|
|
usernameBytes = new Buffer(username, 'ascii');
|
|
|
|
|
encryptedRandomSessionKeyBytes = new Buffer(encryptedRandomSessionKey, 'ascii');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var lmChallengeResponse = calc_resp(create_LM_hashed_password_v1(password), nonce);
|
|
|
|
|
var ntChallengeResponse = calc_resp(create_NT_hashed_password_v1(password), nonce);
|
|
|
|
|
|
|
|
|
|
if(isNegotiateExtendedSecurity){
|
|
|
|
|
var pwhash = create_NT_hashed_password_v1(password);
|
|
|
|
|
var clientChallenge = "";
|
|
|
|
|
for(var i=0; i < 8; i++){
|
|
|
|
|
clientChallenge += String.fromCharCode( Math.floor(Math.random()*256) );
|
|
|
|
|
}
|
|
|
|
|
var clientChallengeBytes = new Buffer(clientChallenge, 'ascii');
|
|
|
|
|
var challenges = ntlm2sr_calc_resp(pwhash, nonce, clientChallengeBytes);
|
|
|
|
|
lmChallengeResponse = challenges.lmChallengeResponse;
|
|
|
|
|
ntChallengeResponse = challenges.ntChallengeResponse;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var signature = 'NTLMSSP\0';
|
|
|
|
|
|
|
|
|
|
var pos = 0;
|
|
|
|
|
var buf = new Buffer(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length + ntChallengeResponse.length + encryptedRandomSessionKeyBytes.length);
|
|
|
|
|
|
|
|
|
|
buf.write(signature, pos, signature.length); pos += signature.length;
|
|
|
|
|
buf.writeUInt32LE(3, pos); pos += 4; // type 1
|
|
|
|
|
|
|
|
|
|
buf.writeUInt16LE(lmChallengeResponse.length, pos); pos += 2; // LmChallengeResponseLen
|
|
|
|
|
buf.writeUInt16LE(lmChallengeResponse.length, pos); pos += 2; // LmChallengeResponseMaxLen
|
|
|
|
|
buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length, pos); pos += 4; // LmChallengeResponseOffset
|
|
|
|
|
|
|
|
|
|
buf.writeUInt16LE(ntChallengeResponse.length, pos); pos += 2; // NtChallengeResponseLen
|
|
|
|
|
buf.writeUInt16LE(ntChallengeResponse.length, pos); pos += 2; // NtChallengeResponseMaxLen
|
|
|
|
|
buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length, pos); pos += 4; // NtChallengeResponseOffset
|
|
|
|
|
|
|
|
|
|
buf.writeUInt16LE(domainNameBytes.length, pos); pos += 2; // DomainNameLen
|
|
|
|
|
buf.writeUInt16LE(domainNameBytes.length, pos); pos += 2; // DomainNameMaxLen
|
|
|
|
|
buf.writeUInt32LE(BODY_LENGTH, pos); pos += 4; // DomainNameOffset
|
|
|
|
|
|
|
|
|
|
buf.writeUInt16LE(usernameBytes.length, pos); pos += 2; // UserNameLen
|
|
|
|
|
buf.writeUInt16LE(usernameBytes.length, pos); pos += 2; // UserNameMaxLen
|
|
|
|
|
buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length, pos); pos += 4; // UserNameOffset
|
|
|
|
|
|
|
|
|
|
buf.writeUInt16LE(workstationBytes.length, pos); pos += 2; // WorkstationLen
|
|
|
|
|
buf.writeUInt16LE(workstationBytes.length, pos); pos += 2; // WorkstationMaxLen
|
|
|
|
|
buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length, pos); pos += 4; // WorkstationOffset
|
|
|
|
|
|
|
|
|
|
buf.writeUInt16LE(encryptedRandomSessionKeyBytes.length, pos); pos += 2; // EncryptedRandomSessionKeyLen
|
|
|
|
|
buf.writeUInt16LE(encryptedRandomSessionKeyBytes.length, pos); pos += 2; // EncryptedRandomSessionKeyMaxLen
|
|
|
|
|
buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length + ntChallengeResponse.length, pos); pos += 4; // EncryptedRandomSessionKeyOffset
|
|
|
|
|
|
|
|
|
|
buf.writeUInt32LE(typeflags.NTLM_TYPE2_FLAGS, pos); pos += 4; // NegotiateFlags
|
|
|
|
|
|
|
|
|
|
buf.writeUInt8(5, pos); pos++; // ProductMajorVersion
|
|
|
|
|
buf.writeUInt8(1, pos); pos++; // ProductMinorVersion
|
|
|
|
|
buf.writeUInt16LE(2600, pos); pos += 2; // ProductBuild
|
|
|
|
|
buf.writeUInt8(0, pos); pos++; // VersionReserved1
|
|
|
|
|
buf.writeUInt8(0, pos); pos++; // VersionReserved2
|
|
|
|
|
buf.writeUInt8(0, pos); pos++; // VersionReserved3
|
|
|
|
|
buf.writeUInt8(15, pos); pos++; // NTLMRevisionCurrent
|
|
|
|
|
|
|
|
|
|
domainNameBytes.copy(buf, pos); pos += domainNameBytes.length;
|
|
|
|
|
usernameBytes.copy(buf, pos); pos += usernameBytes.length;
|
|
|
|
|
workstationBytes.copy(buf, pos); pos += workstationBytes.length;
|
|
|
|
|
lmChallengeResponse.copy(buf, pos); pos += lmChallengeResponse.length;
|
|
|
|
|
ntChallengeResponse.copy(buf, pos); pos += ntChallengeResponse.length;
|
|
|
|
|
encryptedRandomSessionKeyBytes.copy(buf, pos); pos += encryptedRandomSessionKeyBytes.length;
|
|
|
|
|
|
|
|
|
|
return 'NTLM ' + buf.toString('base64');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function create_LM_hashed_password_v1(password){
|
|
|
|
|
// fix the password length to 14 bytes
|
|
|
|
|
password = password.toUpperCase();
|
|
|
|
|
var passwordBytes = new Buffer(password, 'ascii');
|
|
|
|
|
|
|
|
|
|
var passwordBytesPadded = new Buffer(14);
|
|
|
|
|
passwordBytesPadded.fill("\0");
|
|
|
|
|
var sourceEnd = 14;
|
|
|
|
|
if(passwordBytes.length < 14) sourceEnd = passwordBytes.length;
|
|
|
|
|
passwordBytes.copy(passwordBytesPadded, 0, 0, sourceEnd);
|
|
|
|
|
|
|
|
|
|
// split into 2 parts of 7 bytes:
|
|
|
|
|
var firstPart = passwordBytesPadded.slice(0,7);
|
|
|
|
|
var secondPart = passwordBytesPadded.slice(7);
|
|
|
|
|
|
|
|
|
|
function encrypt(buf){
|
|
|
|
|
var key = insertZerosEvery7Bits(buf);
|
|
|
|
|
var des = crypto.createCipheriv('DES-ECB', key, '');
|
|
|
|
|
return des.update("KGS!@#$%"); // page 57 in [MS-NLMP]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var firstPartEncrypted = encrypt(firstPart);
|
|
|
|
|
var secondPartEncrypted = encrypt(secondPart);
|
|
|
|
|
|
|
|
|
|
return Buffer.concat([firstPartEncrypted, secondPartEncrypted]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function insertZerosEvery7Bits(buf){
|
|
|
|
|
var binaryArray = bytes2binaryArray(buf);
|
|
|
|
|
var newBinaryArray = [];
|
|
|
|
|
for(var i=0; i<binaryArray.length; i++){
|
|
|
|
|
newBinaryArray.push(binaryArray[i]);
|
|
|
|
|
|
|
|
|
|
if((i+1)%7 === 0){
|
|
|
|
|
newBinaryArray.push(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return binaryArray2bytes(newBinaryArray);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function bytes2binaryArray(buf){
|
|
|
|
|
var hex2binary = {
|
|
|
|
|
0: [0,0,0,0],
|
|
|
|
|
1: [0,0,0,1],
|
|
|
|
|
2: [0,0,1,0],
|
|
|
|
|
3: [0,0,1,1],
|
|
|
|
|
4: [0,1,0,0],
|
|
|
|
|
5: [0,1,0,1],
|
|
|
|
|
6: [0,1,1,0],
|
|
|
|
|
7: [0,1,1,1],
|
|
|
|
|
8: [1,0,0,0],
|
|
|
|
|
9: [1,0,0,1],
|
|
|
|
|
A: [1,0,1,0],
|
|
|
|
|
B: [1,0,1,1],
|
|
|
|
|
C: [1,1,0,0],
|
|
|
|
|
D: [1,1,0,1],
|
|
|
|
|
E: [1,1,1,0],
|
|
|
|
|
F: [1,1,1,1]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var hexString = buf.toString('hex').toUpperCase();
|
|
|
|
|
var array = [];
|
|
|
|
|
for(var i=0; i<hexString.length; i++){
|
|
|
|
|
var hexchar = hexString.charAt(i);
|
|
|
|
|
array = array.concat(hex2binary[hexchar]);
|
|
|
|
|
}
|
|
|
|
|
return array;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function binaryArray2bytes(array){
|
|
|
|
|
var binary2hex = {
|
|
|
|
|
'0000': 0,
|
|
|
|
|
'0001': 1,
|
|
|
|
|
'0010': 2,
|
|
|
|
|
'0011': 3,
|
|
|
|
|
'0100': 4,
|
|
|
|
|
'0101': 5,
|
|
|
|
|
'0110': 6,
|
|
|
|
|
'0111': 7,
|
|
|
|
|
'1000': 8,
|
|
|
|
|
'1001': 9,
|
|
|
|
|
'1010': 'A',
|
|
|
|
|
'1011': 'B',
|
|
|
|
|
'1100': 'C',
|
|
|
|
|
'1101': 'D',
|
|
|
|
|
'1110': 'E',
|
|
|
|
|
'1111': 'F'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var bufArray = [];
|
|
|
|
|
|
|
|
|
|
for(var i=0; i<array.length; i +=8 ){
|
|
|
|
|
if((i+7) > array.length)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
var binString1 = '' + array[i] + '' + array[i+1] + '' + array[i+2] + '' + array[i+3];
|
|
|
|
|
var binString2 = '' + array[i+4] + '' + array[i+5] + '' + array[i+6] + '' + array[i+7];
|
|
|
|
|
var hexchar1 = binary2hex[binString1];
|
|
|
|
|
var hexchar2 = binary2hex[binString2];
|
|
|
|
|
|
|
|
|
|
var buf = new Buffer(hexchar1 + '' + hexchar2, 'hex');
|
|
|
|
|
bufArray.push(buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Buffer.concat(bufArray);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function create_NT_hashed_password_v1(password){
|
|
|
|
|
var buf = new Buffer(password, 'utf16le');
|
|
|
|
|
var md4 = crypto.createHash('md4');
|
|
|
|
|
md4.update(buf);
|
|
|
|
|
return new Buffer(md4.digest());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function calc_resp(password_hash, server_challenge){
|
|
|
|
|
// padding with zeros to make the hash 21 bytes long
|
|
|
|
|
var passHashPadded = new Buffer(21);
|
|
|
|
|
passHashPadded.fill("\0");
|
|
|
|
|
password_hash.copy(passHashPadded, 0, 0, password_hash.length);
|
|
|
|
|
|
|
|
|
|
var resArray = [];
|
|
|
|
|
|
|
|
|
|
var des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(0,7)), '');
|
|
|
|
|
resArray.push( des.update(server_challenge.slice(0,8)) );
|
|
|
|
|
|
|
|
|
|
des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(7,14)), '');
|
|
|
|
|
resArray.push( des.update(server_challenge.slice(0,8)) );
|
|
|
|
|
|
|
|
|
|
des = crypto.createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(14,21)), '');
|
|
|
|
|
resArray.push( des.update(server_challenge.slice(0,8)) );
|
|
|
|
|
|
|
|
|
|
return Buffer.concat(resArray);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ntlm2sr_calc_resp(responseKeyNT, serverChallenge, clientChallenge){
|
|
|
|
|
// padding with zeros to make the hash 16 bytes longer
|
|
|
|
|
var lmChallengeResponse = new Buffer(clientChallenge.length + 16);
|
|
|
|
|
lmChallengeResponse.fill("\0");
|
|
|
|
|
clientChallenge.copy(lmChallengeResponse, 0, 0, clientChallenge.length);
|
|
|
|
|
|
|
|
|
|
var buf = Buffer.concat([serverChallenge, clientChallenge]);
|
|
|
|
|
var md5 = crypto.createHash('md5');
|
|
|
|
|
md5.update(buf);
|
|
|
|
|
var sess = md5.digest();
|
|
|
|
|
var ntChallengeResponse = calc_resp(responseKeyNT, sess.slice(0,8));
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
lmChallengeResponse: lmChallengeResponse,
|
|
|
|
|
ntChallengeResponse: ntChallengeResponse
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exports.createType1Message = createType1Message;
|
|
|
|
|
exports.parseType2Message = parseType2Message;
|
|
|
|
|
exports.createType3Message = createType3Message;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 443:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
|
|
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
|
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
|
|
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
|
|
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
|
|
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
|
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
|
|
if (mod && mod.__esModule) return mod;
|
|
|
|
|
var result = {};
|
|
|
|
|
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
|
|
|
result["default"] = mod;
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
const core = __importStar(__webpack_require__(470));
|
|
|
|
|
const io = __importStar(__webpack_require__(1));
|
|
|
|
|
const fs = __importStar(__webpack_require__(747));
|
|
|
|
|
const os = __importStar(__webpack_require__(87));
|
|
|
|
|
const path = __importStar(__webpack_require__(622));
|
|
|
|
|
const uuidV4 = __importStar(__webpack_require__(826));
|
|
|
|
|
const constants_1 = __webpack_require__(694);
|
|
|
|
|
// From https://github.com/actions/toolkit/blob/master/packages/tool-cache/src/tool-cache.ts#L23
|
|
|
|
|
function createTempDirectory() {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
const IS_WINDOWS = process.platform === "win32";
|
|
|
|
|
let tempDirectory = process.env["RUNNER_TEMP"] || "";
|
|
|
|
|
if (!tempDirectory) {
|
|
|
|
|
let baseLocation;
|
|
|
|
|
if (IS_WINDOWS) {
|
|
|
|
|
// On Windows use the USERPROFILE env variable
|
|
|
|
|
baseLocation = process.env["USERPROFILE"] || "C:\\";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (process.platform === "darwin") {
|
|
|
|
|
baseLocation = "/Users";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
baseLocation = "/home";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
tempDirectory = path.join(baseLocation, "actions", "temp");
|
|
|
|
|
}
|
|
|
|
|
const dest = path.join(tempDirectory, uuidV4.default());
|
|
|
|
|
yield io.mkdirP(dest);
|
|
|
|
|
return dest;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.createTempDirectory = createTempDirectory;
|
|
|
|
|
function getArchiveFileSize(path) {
|
|
|
|
|
return fs.statSync(path).size;
|
|
|
|
|
}
|
|
|
|
|
exports.getArchiveFileSize = getArchiveFileSize;
|
|
|
|
|
function isExactKeyMatch(key, cacheResult) {
|
|
|
|
|
return !!(cacheResult &&
|
|
|
|
|
cacheResult.cacheKey &&
|
|
|
|
|
cacheResult.cacheKey.localeCompare(key, undefined, {
|
|
|
|
|
sensitivity: "accent"
|
|
|
|
|
}) === 0);
|
|
|
|
|
}
|
|
|
|
|
exports.isExactKeyMatch = isExactKeyMatch;
|
|
|
|
|
function setCacheState(state) {
|
|
|
|
|
core.saveState(constants_1.State.CacheResult, JSON.stringify(state));
|
|
|
|
|
}
|
|
|
|
|
exports.setCacheState = setCacheState;
|
|
|
|
|
function setCacheHitOutput(isCacheHit) {
|
|
|
|
|
core.setOutput(constants_1.Outputs.CacheHit, isCacheHit.toString());
|
|
|
|
|
}
|
|
|
|
|
exports.setCacheHitOutput = setCacheHitOutput;
|
|
|
|
|
function setOutputAndState(key, cacheResult) {
|
|
|
|
|
setCacheHitOutput(isExactKeyMatch(key, cacheResult));
|
|
|
|
|
// Store the cache result if it exists
|
|
|
|
|
cacheResult && setCacheState(cacheResult);
|
|
|
|
|
}
|
|
|
|
|
exports.setOutputAndState = setOutputAndState;
|
|
|
|
|
function getCacheState() {
|
|
|
|
|
const stateData = core.getState(constants_1.State.CacheResult);
|
|
|
|
|
core.debug(`State: ${stateData}`);
|
|
|
|
|
if (stateData) {
|
|
|
|
|
return JSON.parse(stateData);
|
|
|
|
|
}
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
exports.getCacheState = getCacheState;
|
|
|
|
|
function logWarning(message) {
|
|
|
|
|
const warningPrefix = "[warning]";
|
|
|
|
|
core.info(`${warningPrefix}${message}`);
|
|
|
|
|
}
|
|
|
|
|
exports.logWarning = logWarning;
|
|
|
|
|
function resolvePath(filePath) {
|
|
|
|
|
if (filePath[0] === "~") {
|
|
|
|
|
const home = os.homedir();
|
|
|
|
|
if (!home) {
|
|
|
|
|
throw new Error("Unable to resolve `~` to HOME");
|
|
|
|
|
}
|
|
|
|
|
return path.join(home, filePath.slice(1));
|
|
|
|
|
}
|
|
|
|
|
return path.resolve(filePath);
|
|
|
|
|
}
|
|
|
|
|
exports.resolvePath = resolvePath;
|
|
|
|
|
function getSupportedEvents() {
|
|
|
|
|
return [constants_1.Events.Push, constants_1.Events.PullRequest];
|
|
|
|
|
}
|
|
|
|
|
exports.getSupportedEvents = getSupportedEvents;
|
|
|
|
|
// Currently the cache token is only authorized for push and pull_request events
|
|
|
|
|
// All other events will fail when reading and saving the cache
|
|
|
|
|
// See GitHub Context https://help.github.com/actions/automating-your-workflow-with-github-actions/contexts-and-expression-syntax-for-github-actions#github-context
|
|
|
|
|
function isValidEvent() {
|
|
|
|
|
const githubEvent = process.env[constants_1.Events.Key] || "";
|
|
|
|
|
return getSupportedEvents().includes(githubEvent);
|
|
|
|
|
}
|
|
|
|
|
exports.isValidEvent = isValidEvent;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 470:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
|
|
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
|
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
|
|
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
|
|
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
|
|
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
|
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
const command_1 = __webpack_require__(431);
|
|
|
|
|
const os = __webpack_require__(87);
|
|
|
|
|
const path = __webpack_require__(622);
|
|
|
|
|
/**
|
|
|
|
|
* The code to exit an action
|
|
|
|
|
*/
|
|
|
|
|
var ExitCode;
|
|
|
|
|
(function (ExitCode) {
|
|
|
|
|
/**
|
|
|
|
|
* A code indicating that the action was successful
|
|
|
|
|
*/
|
|
|
|
|
ExitCode[ExitCode["Success"] = 0] = "Success";
|
|
|
|
|
/**
|
|
|
|
|
* A code indicating that the action was a failure
|
|
|
|
|
*/
|
|
|
|
|
ExitCode[ExitCode["Failure"] = 1] = "Failure";
|
|
|
|
|
})(ExitCode = exports.ExitCode || (exports.ExitCode = {}));
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
|
// Variables
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
|
/**
|
|
|
|
|
* Sets env variable for this action and future actions in the job
|
|
|
|
|
* @param name the name of the variable to set
|
|
|
|
|
* @param val the value of the variable
|
|
|
|
|
*/
|
|
|
|
|
function exportVariable(name, val) {
|
|
|
|
|
process.env[name] = val;
|
|
|
|
|
command_1.issueCommand('set-env', { name }, val);
|
|
|
|
|
}
|
|
|
|
|
exports.exportVariable = exportVariable;
|
|
|
|
|
/**
|
|
|
|
|
* Registers a secret which will get masked from logs
|
|
|
|
|
* @param secret value of the secret
|
|
|
|
|
*/
|
|
|
|
|
function setSecret(secret) {
|
|
|
|
|
command_1.issueCommand('add-mask', {}, secret);
|
|
|
|
|
}
|
|
|
|
|
exports.setSecret = setSecret;
|
|
|
|
|
/**
|
|
|
|
|
* Prepends inputPath to the PATH (for this action and future actions)
|
|
|
|
|
* @param inputPath
|
|
|
|
|
*/
|
|
|
|
|
function addPath(inputPath) {
|
|
|
|
|
command_1.issueCommand('add-path', {}, inputPath);
|
|
|
|
|
process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`;
|
|
|
|
|
}
|
|
|
|
|
exports.addPath = addPath;
|
|
|
|
|
/**
|
|
|
|
|
* Gets the value of an input. The value is also trimmed.
|
|
|
|
|
*
|
|
|
|
|
* @param name name of the input to get
|
|
|
|
|
* @param options optional. See InputOptions.
|
|
|
|
|
* @returns string
|
|
|
|
|
*/
|
|
|
|
|
function getInput(name, options) {
|
|
|
|
|
const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
|
|
|
|
|
if (options && options.required && !val) {
|
|
|
|
|
throw new Error(`Input required and not supplied: ${name}`);
|
|
|
|
|
}
|
|
|
|
|
return val.trim();
|
|
|
|
|
}
|
|
|
|
|
exports.getInput = getInput;
|
|
|
|
|
/**
|
|
|
|
|
* Sets the value of an output.
|
|
|
|
|
*
|
|
|
|
|
* @param name name of the output to set
|
|
|
|
|
* @param value value to store
|
|
|
|
|
*/
|
|
|
|
|
function setOutput(name, value) {
|
|
|
|
|
command_1.issueCommand('set-output', { name }, value);
|
|
|
|
|
}
|
|
|
|
|
exports.setOutput = setOutput;
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
|
// Results
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
|
/**
|
|
|
|
|
* Sets the action status to failed.
|
|
|
|
|
* When the action exits it will be with an exit code of 1
|
|
|
|
|
* @param message add error issue message
|
|
|
|
|
*/
|
|
|
|
|
function setFailed(message) {
|
|
|
|
|
process.exitCode = ExitCode.Failure;
|
|
|
|
|
error(message);
|
|
|
|
|
}
|
|
|
|
|
exports.setFailed = setFailed;
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
|
// Logging Commands
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
|
/**
|
|
|
|
|
* Writes debug message to user log
|
|
|
|
|
* @param message debug message
|
|
|
|
|
*/
|
|
|
|
|
function debug(message) {
|
|
|
|
|
command_1.issueCommand('debug', {}, message);
|
|
|
|
|
}
|
|
|
|
|
exports.debug = debug;
|
|
|
|
|
/**
|
|
|
|
|
* Adds an error issue
|
|
|
|
|
* @param message error issue message
|
|
|
|
|
*/
|
|
|
|
|
function error(message) {
|
|
|
|
|
command_1.issue('error', message);
|
|
|
|
|
}
|
|
|
|
|
exports.error = error;
|
|
|
|
|
/**
|
|
|
|
|
* Adds an warning issue
|
|
|
|
|
* @param message warning issue message
|
|
|
|
|
*/
|
|
|
|
|
function warning(message) {
|
|
|
|
|
command_1.issue('warning', message);
|
|
|
|
|
}
|
|
|
|
|
exports.warning = warning;
|
|
|
|
|
/**
|
|
|
|
|
* Writes info to log with console.log.
|
|
|
|
|
* @param message info message
|
|
|
|
|
*/
|
|
|
|
|
function info(message) {
|
|
|
|
|
process.stdout.write(message + os.EOL);
|
|
|
|
|
}
|
|
|
|
|
exports.info = info;
|
|
|
|
|
/**
|
|
|
|
|
* Begin an output group.
|
|
|
|
|
*
|
|
|
|
|
* Output until the next `groupEnd` will be foldable in this group
|
|
|
|
|
*
|
|
|
|
|
* @param name The name of the output group
|
|
|
|
|
*/
|
|
|
|
|
function startGroup(name) {
|
|
|
|
|
command_1.issue('group', name);
|
|
|
|
|
}
|
|
|
|
|
exports.startGroup = startGroup;
|
|
|
|
|
/**
|
|
|
|
|
* End an output group.
|
|
|
|
|
*/
|
|
|
|
|
function endGroup() {
|
|
|
|
|
command_1.issue('endgroup');
|
|
|
|
|
}
|
|
|
|
|
exports.endGroup = endGroup;
|
|
|
|
|
/**
|
|
|
|
|
* Wrap an asynchronous function call in a group.
|
|
|
|
|
*
|
|
|
|
|
* Returns the same type as the function itself.
|
|
|
|
|
*
|
|
|
|
|
* @param name The name of the group
|
|
|
|
|
* @param fn The function to wrap in the group
|
|
|
|
|
*/
|
|
|
|
|
function group(name, fn) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
startGroup(name);
|
|
|
|
|
let result;
|
|
|
|
|
try {
|
|
|
|
|
result = yield fn();
|
|
|
|
|
}
|
|
|
|
|
finally {
|
|
|
|
|
endGroup();
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.group = group;
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
|
// Wrapper action state
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
|
/**
|
|
|
|
|
* Saves state for current action, the state can only be retrieved by this action's post job execution.
|
|
|
|
|
*
|
|
|
|
|
* @param name name of the state to store
|
|
|
|
|
* @param value value to store
|
|
|
|
|
*/
|
|
|
|
|
function saveState(name, value) {
|
|
|
|
|
command_1.issueCommand('save-state', { name }, value);
|
|
|
|
|
}
|
|
|
|
|
exports.saveState = saveState;
|
|
|
|
|
/**
|
|
|
|
|
* Gets the value of an state set by this action's main execution.
|
|
|
|
|
*
|
|
|
|
|
* @param name name of the state to get
|
|
|
|
|
* @returns string
|
|
|
|
|
*/
|
|
|
|
|
function getState(name) {
|
|
|
|
|
return process.env[`STATE_${name}`] || '';
|
|
|
|
|
}
|
|
|
|
|
exports.getState = getState;
|
|
|
|
|
//# sourceMappingURL=core.js.map
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 525:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
const http = __webpack_require__(605);
|
|
|
|
|
const https = __webpack_require__(211);
|
|
|
|
|
const _ = __webpack_require__(891);
|
|
|
|
|
const ntlm = __webpack_require__(432);
|
|
|
|
|
class NtlmCredentialHandler {
|
|
|
|
|
constructor(username, password, workstation, domain) {
|
|
|
|
|
this._ntlmOptions = {};
|
|
|
|
|
this._ntlmOptions.username = username;
|
|
|
|
|
this._ntlmOptions.password = password;
|
|
|
|
|
if (domain !== undefined) {
|
|
|
|
|
this._ntlmOptions.domain = domain;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
this._ntlmOptions.domain = '';
|
|
|
|
|
}
|
|
|
|
|
if (workstation !== undefined) {
|
|
|
|
|
this._ntlmOptions.workstation = workstation;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
this._ntlmOptions.workstation = '';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
prepareRequest(options) {
|
|
|
|
|
// No headers or options need to be set. We keep the credentials on the handler itself.
|
|
|
|
|
// If a (proxy) agent is set, remove it as we don't support proxy for NTLM at this time
|
|
|
|
|
if (options.agent) {
|
|
|
|
|
delete options.agent;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
canHandleAuthentication(response) {
|
|
|
|
|
if (response && response.message && response.message.statusCode === 401) {
|
|
|
|
|
// Ensure that we're talking NTLM here
|
|
|
|
|
// Once we have the www-authenticate header, split it so we can ensure we can talk NTLM
|
|
|
|
|
const wwwAuthenticate = response.message.headers['www-authenticate'];
|
|
|
|
|
if (wwwAuthenticate) {
|
|
|
|
|
const mechanisms = wwwAuthenticate.split(', ');
|
|
|
|
|
const index = mechanisms.indexOf("NTLM");
|
|
|
|
|
if (index >= 0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
handleAuthentication(httpClient, requestInfo, objs) {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
const callbackForResult = function (err, res) {
|
|
|
|
|
if (err) {
|
|
|
|
|
reject(err);
|
|
|
|
|
}
|
|
|
|
|
// We have to readbody on the response before continuing otherwise there is a hang.
|
|
|
|
|
res.readBody().then(() => {
|
|
|
|
|
resolve(res);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
this.handleAuthenticationPrivate(httpClient, requestInfo, objs, callbackForResult);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
handleAuthenticationPrivate(httpClient, requestInfo, objs, finalCallback) {
|
|
|
|
|
// Set up the headers for NTLM authentication
|
|
|
|
|
requestInfo.options = _.extend(requestInfo.options, {
|
|
|
|
|
username: this._ntlmOptions.username,
|
|
|
|
|
password: this._ntlmOptions.password,
|
|
|
|
|
domain: this._ntlmOptions.domain,
|
|
|
|
|
workstation: this._ntlmOptions.workstation
|
|
|
|
|
});
|
|
|
|
|
if (httpClient.isSsl === true) {
|
|
|
|
|
requestInfo.options.agent = new https.Agent({ keepAlive: true });
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
requestInfo.options.agent = new http.Agent({ keepAlive: true });
|
|
|
|
|
}
|
|
|
|
|
let self = this;
|
|
|
|
|
// The following pattern of sending the type1 message following immediately (in a setImmediate) is
|
|
|
|
|
// critical for the NTLM exchange to happen. If we removed setImmediate (or call in a different manner)
|
|
|
|
|
// the NTLM exchange will always fail with a 401.
|
|
|
|
|
this.sendType1Message(httpClient, requestInfo, objs, function (err, res) {
|
|
|
|
|
if (err) {
|
|
|
|
|
return finalCallback(err, null, null);
|
|
|
|
|
}
|
|
|
|
|
/// We have to readbody on the response before continuing otherwise there is a hang.
|
|
|
|
|
res.readBody().then(() => {
|
|
|
|
|
// It is critical that we have setImmediate here due to how connection requests are queued.
|
|
|
|
|
// If setImmediate is removed then the NTLM handshake will not work.
|
|
|
|
|
// setImmediate allows us to queue a second request on the same connection. If this second
|
|
|
|
|
// request is not queued on the connection when the first request finishes then node closes
|
|
|
|
|
// the connection. NTLM requires both requests to be on the same connection so we need this.
|
|
|
|
|
setImmediate(function () {
|
|
|
|
|
self.sendType3Message(httpClient, requestInfo, objs, res, finalCallback);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// The following method is an adaptation of code found at https://github.com/SamDecrock/node-http-ntlm/blob/master/httpntlm.js
|
|
|
|
|
sendType1Message(httpClient, requestInfo, objs, finalCallback) {
|
|
|
|
|
const type1msg = ntlm.createType1Message(this._ntlmOptions);
|
|
|
|
|
const type1options = {
|
|
|
|
|
headers: {
|
|
|
|
|
'Connection': 'keep-alive',
|
|
|
|
|
'Authorization': type1msg
|
|
|
|
|
},
|
|
|
|
|
timeout: requestInfo.options.timeout || 0,
|
|
|
|
|
agent: requestInfo.httpModule,
|
|
|
|
|
};
|
|
|
|
|
const type1info = {};
|
|
|
|
|
type1info.httpModule = requestInfo.httpModule;
|
|
|
|
|
type1info.parsedUrl = requestInfo.parsedUrl;
|
|
|
|
|
type1info.options = _.extend(type1options, _.omit(requestInfo.options, 'headers'));
|
|
|
|
|
return httpClient.requestRawWithCallback(type1info, objs, finalCallback);
|
|
|
|
|
}
|
|
|
|
|
// The following method is an adaptation of code found at https://github.com/SamDecrock/node-http-ntlm/blob/master/httpntlm.js
|
|
|
|
|
sendType3Message(httpClient, requestInfo, objs, res, callback) {
|
|
|
|
|
if (!res.message.headers && !res.message.headers['www-authenticate']) {
|
|
|
|
|
throw new Error('www-authenticate not found on response of second request');
|
|
|
|
|
}
|
|
|
|
|
const type2msg = ntlm.parseType2Message(res.message.headers['www-authenticate']);
|
|
|
|
|
const type3msg = ntlm.createType3Message(type2msg, this._ntlmOptions);
|
|
|
|
|
const type3options = {
|
|
|
|
|
headers: {
|
|
|
|
|
'Authorization': type3msg,
|
|
|
|
|
'Connection': 'Close'
|
|
|
|
|
},
|
|
|
|
|
agent: requestInfo.httpModule,
|
|
|
|
|
};
|
|
|
|
|
const type3info = {};
|
|
|
|
|
type3info.httpModule = requestInfo.httpModule;
|
|
|
|
|
type3info.parsedUrl = requestInfo.parsedUrl;
|
|
|
|
|
type3options.headers = _.extend(type3options.headers, requestInfo.options.headers);
|
|
|
|
|
type3info.options = _.extend(type3options, _.omit(requestInfo.options, 'headers'));
|
|
|
|
|
return httpClient.requestRawWithCallback(type3info, objs, callback);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
exports.NtlmCredentialHandler = NtlmCredentialHandler;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 571:
|
|
|
|
|
/***/ (function(__unusedmodule, exports) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
class BearerCredentialHandler {
|
|
|
|
|
constructor(token) {
|
|
|
|
|
this.token = token;
|
|
|
|
|
}
|
|
|
|
|
// currently implements pre-authorization
|
|
|
|
|
// TODO: support preAuth = false where it hooks on 401
|
|
|
|
|
prepareRequest(options) {
|
|
|
|
|
options.headers['Authorization'] = 'Bearer ' + this.token;
|
|
|
|
|
options.headers['X-TFS-FedAuthRedirect'] = 'Suppress';
|
|
|
|
|
}
|
|
|
|
|
// This handler cannot handle 401
|
|
|
|
|
canHandleAuthentication(response) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
handleAuthentication(httpClient, requestInfo, objs) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
exports.BearerCredentialHandler = BearerCredentialHandler;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 605:
|
|
|
|
|
/***/ (function(module) {
|
|
|
|
|
|
|
|
|
|
module.exports = require("http");
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 614:
|
|
|
|
|
/***/ (function(module) {
|
|
|
|
|
|
|
|
|
|
module.exports = require("events");
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 622:
|
|
|
|
|
/***/ (function(module) {
|
|
|
|
|
|
|
|
|
|
module.exports = require("path");
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 631:
|
|
|
|
|
/***/ (function(module) {
|
|
|
|
|
|
|
|
|
|
module.exports = require("net");
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 669:
|
|
|
|
|
/***/ (function(module) {
|
|
|
|
|
|
|
|
|
|
module.exports = require("util");
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 672:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
|
|
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
|
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
|
|
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
|
|
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
|
|
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
|
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
var _a;
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
const assert_1 = __webpack_require__(357);
|
|
|
|
|
const fs = __webpack_require__(747);
|
|
|
|
|
const path = __webpack_require__(622);
|
|
|
|
|
_a = fs.promises, exports.chmod = _a.chmod, exports.copyFile = _a.copyFile, exports.lstat = _a.lstat, exports.mkdir = _a.mkdir, exports.readdir = _a.readdir, exports.readlink = _a.readlink, exports.rename = _a.rename, exports.rmdir = _a.rmdir, exports.stat = _a.stat, exports.symlink = _a.symlink, exports.unlink = _a.unlink;
|
|
|
|
|
exports.IS_WINDOWS = process.platform === 'win32';
|
|
|
|
|
function exists(fsPath) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
try {
|
|
|
|
|
yield exports.stat(fsPath);
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
|
|
|
|
if (err.code === 'ENOENT') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
throw err;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.exists = exists;
|
|
|
|
|
function isDirectory(fsPath, useStat = false) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
const stats = useStat ? yield exports.stat(fsPath) : yield exports.lstat(fsPath);
|
|
|
|
|
return stats.isDirectory();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.isDirectory = isDirectory;
|
|
|
|
|
/**
|
|
|
|
|
* On OSX/Linux, true if path starts with '/'. On Windows, true for paths like:
|
|
|
|
|
* \, \hello, \\hello\share, C:, and C:\hello (and corresponding alternate separator cases).
|
|
|
|
|
*/
|
|
|
|
|
function isRooted(p) {
|
|
|
|
|
p = normalizeSeparators(p);
|
|
|
|
|
if (!p) {
|
|
|
|
|
throw new Error('isRooted() parameter "p" cannot be empty');
|
|
|
|
|
}
|
|
|
|
|
if (exports.IS_WINDOWS) {
|
|
|
|
|
return (p.startsWith('\\') || /^[A-Z]:/i.test(p) // e.g. \ or \hello or \\hello
|
|
|
|
|
); // e.g. C: or C:\hello
|
|
|
|
|
}
|
|
|
|
|
return p.startsWith('/');
|
|
|
|
|
}
|
|
|
|
|
exports.isRooted = isRooted;
|
|
|
|
|
/**
|
|
|
|
|
* Recursively create a directory at `fsPath`.
|
|
|
|
|
*
|
|
|
|
|
* This implementation is optimistic, meaning it attempts to create the full
|
|
|
|
|
* path first, and backs up the path stack from there.
|
|
|
|
|
*
|
|
|
|
|
* @param fsPath The path to create
|
|
|
|
|
* @param maxDepth The maximum recursion depth
|
|
|
|
|
* @param depth The current recursion depth
|
|
|
|
|
*/
|
|
|
|
|
function mkdirP(fsPath, maxDepth = 1000, depth = 1) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
assert_1.ok(fsPath, 'a path argument must be provided');
|
|
|
|
|
fsPath = path.resolve(fsPath);
|
|
|
|
|
if (depth >= maxDepth)
|
|
|
|
|
return exports.mkdir(fsPath);
|
|
|
|
|
try {
|
|
|
|
|
yield exports.mkdir(fsPath);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
|
|
|
|
switch (err.code) {
|
|
|
|
|
case 'ENOENT': {
|
|
|
|
|
yield mkdirP(path.dirname(fsPath), maxDepth, depth + 1);
|
|
|
|
|
yield exports.mkdir(fsPath);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
let stats;
|
|
|
|
|
try {
|
|
|
|
|
stats = yield exports.stat(fsPath);
|
|
|
|
|
}
|
|
|
|
|
catch (err2) {
|
|
|
|
|
throw err;
|
|
|
|
|
}
|
|
|
|
|
if (!stats.isDirectory())
|
|
|
|
|
throw err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.mkdirP = mkdirP;
|
|
|
|
|
/**
|
|
|
|
|
* Best effort attempt to determine whether a file exists and is executable.
|
|
|
|
|
* @param filePath file path to check
|
|
|
|
|
* @param extensions additional file extensions to try
|
|
|
|
|
* @return if file exists and is executable, returns the file path. otherwise empty string.
|
|
|
|
|
*/
|
|
|
|
|
function tryGetExecutablePath(filePath, extensions) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
let stats = undefined;
|
|
|
|
|
try {
|
|
|
|
|
// test file exists
|
|
|
|
|
stats = yield exports.stat(filePath);
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
|
|
|
|
if (err.code !== 'ENOENT') {
|
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
|
console.log(`Unexpected error attempting to determine if executable file exists '${filePath}': ${err}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (stats && stats.isFile()) {
|
|
|
|
|
if (exports.IS_WINDOWS) {
|
|
|
|
|
// on Windows, test for valid extension
|
|
|
|
|
const upperExt = path.extname(filePath).toUpperCase();
|
|
|
|
|
if (extensions.some(validExt => validExt.toUpperCase() === upperExt)) {
|
|
|
|
|
return filePath;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (isUnixExecutable(stats)) {
|
|
|
|
|
return filePath;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// try each extension
|
|
|
|
|
const originalFilePath = filePath;
|
|
|
|
|
for (const extension of extensions) {
|
|
|
|
|
filePath = originalFilePath + extension;
|
|
|
|
|
stats = undefined;
|
|
|
|
|
try {
|
|
|
|
|
stats = yield exports.stat(filePath);
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
|
|
|
|
if (err.code !== 'ENOENT') {
|
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
|
console.log(`Unexpected error attempting to determine if executable file exists '${filePath}': ${err}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (stats && stats.isFile()) {
|
|
|
|
|
if (exports.IS_WINDOWS) {
|
|
|
|
|
// preserve the case of the actual file (since an extension was appended)
|
|
|
|
|
try {
|
|
|
|
|
const directory = path.dirname(filePath);
|
|
|
|
|
const upperName = path.basename(filePath).toUpperCase();
|
|
|
|
|
for (const actualName of yield exports.readdir(directory)) {
|
|
|
|
|
if (upperName === actualName.toUpperCase()) {
|
|
|
|
|
filePath = path.join(directory, actualName);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
|
console.log(`Unexpected error attempting to determine the actual case of the file '${filePath}': ${err}`);
|
|
|
|
|
}
|
|
|
|
|
return filePath;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (isUnixExecutable(stats)) {
|
|
|
|
|
return filePath;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return '';
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.tryGetExecutablePath = tryGetExecutablePath;
|
|
|
|
|
function normalizeSeparators(p) {
|
|
|
|
|
p = p || '';
|
|
|
|
|
if (exports.IS_WINDOWS) {
|
|
|
|
|
// convert slashes on Windows
|
|
|
|
|
p = p.replace(/\//g, '\\');
|
|
|
|
|
// remove redundant slashes
|
|
|
|
|
return p.replace(/\\\\+/g, '\\');
|
|
|
|
|
}
|
|
|
|
|
// remove redundant slashes
|
|
|
|
|
return p.replace(/\/\/+/g, '/');
|
|
|
|
|
}
|
|
|
|
|
// on Mac/Linux, test the execute bit
|
|
|
|
|
// R W X R W X R W X
|
|
|
|
|
// 256 128 64 32 16 8 4 2 1
|
|
|
|
|
function isUnixExecutable(stats) {
|
|
|
|
|
return ((stats.mode & 1) > 0 ||
|
|
|
|
|
((stats.mode & 8) > 0 && stats.gid === process.getgid()) ||
|
|
|
|
|
((stats.mode & 64) > 0 && stats.uid === process.getuid()));
|
|
|
|
|
}
|
|
|
|
|
//# sourceMappingURL=io-util.js.map
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 681:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
|
|
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
|
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
|
|
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
|
|
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
|
|
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
|
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
|
|
if (mod && mod.__esModule) return mod;
|
|
|
|
|
var result = {};
|
|
|
|
|
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
|
|
|
result["default"] = mod;
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
const core = __importStar(__webpack_require__(470));
|
|
|
|
|
const path = __importStar(__webpack_require__(622));
|
|
|
|
|
const cacheHttpClient = __importStar(__webpack_require__(154));
|
|
|
|
|
const constants_1 = __webpack_require__(694);
|
|
|
|
|
const tar_1 = __webpack_require__(943);
|
|
|
|
|
const utils = __importStar(__webpack_require__(443));
|
|
|
|
|
function run() {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
try {
|
|
|
|
|
if (!utils.isValidEvent()) {
|
|
|
|
|
utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported. Only ${utils
|
|
|
|
|
.getSupportedEvents()
|
|
|
|
|
.join(", ")} events are supported at this time.`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const state = utils.getCacheState();
|
|
|
|
|
// Inputs are re-evaluted before the post action, so we want the original key used for restore
|
|
|
|
|
const primaryKey = core.getState(constants_1.State.CacheKey);
|
|
|
|
|
if (!primaryKey) {
|
|
|
|
|
utils.logWarning(`Error retrieving key from state.`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (utils.isExactKeyMatch(primaryKey, state)) {
|
|
|
|
|
core.info(`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const cachePath = utils.resolvePath(core.getInput(constants_1.Inputs.Path, { required: true }));
|
|
|
|
|
core.debug(`Cache Path: ${cachePath}`);
|
|
|
|
|
const archivePath = path.join(yield utils.createTempDirectory(), "cache.tgz");
|
|
|
|
|
core.debug(`Archive Path: ${archivePath}`);
|
|
|
|
|
yield tar_1.createTar(archivePath, cachePath);
|
|
|
|
|
const fileSizeLimit = 400 * 1024 * 1024; // 400MB
|
|
|
|
|
const archiveFileSize = utils.getArchiveFileSize(archivePath);
|
|
|
|
|
core.debug(`File Size: ${archiveFileSize}`);
|
|
|
|
|
if (archiveFileSize > fileSizeLimit) {
|
|
|
|
|
utils.logWarning(`Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the 400MB limit, not saving cache.`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
yield cacheHttpClient.saveCache(primaryKey, archivePath);
|
|
|
|
|
}
|
|
|
|
|
catch (error) {
|
|
|
|
|
utils.logWarning(error.message);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
run();
|
|
|
|
|
exports.default = run;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 694:
|
|
|
|
|
/***/ (function(__unusedmodule, exports) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
var Inputs;
|
|
|
|
|
(function (Inputs) {
|
|
|
|
|
Inputs["Key"] = "key";
|
|
|
|
|
Inputs["Path"] = "path";
|
|
|
|
|
Inputs["RestoreKeys"] = "restore-keys";
|
|
|
|
|
})(Inputs = exports.Inputs || (exports.Inputs = {}));
|
|
|
|
|
var Outputs;
|
|
|
|
|
(function (Outputs) {
|
|
|
|
|
Outputs["CacheHit"] = "cache-hit";
|
|
|
|
|
})(Outputs = exports.Outputs || (exports.Outputs = {}));
|
|
|
|
|
var State;
|
|
|
|
|
(function (State) {
|
|
|
|
|
State["CacheKey"] = "CACHE_KEY";
|
|
|
|
|
State["CacheResult"] = "CACHE_RESULT";
|
|
|
|
|
})(State = exports.State || (exports.State = {}));
|
|
|
|
|
var Events;
|
|
|
|
|
(function (Events) {
|
|
|
|
|
Events["Key"] = "GITHUB_EVENT_NAME";
|
|
|
|
|
Events["Push"] = "push";
|
|
|
|
|
Events["PullRequest"] = "pull_request";
|
|
|
|
|
})(Events = exports.Events || (exports.Events = {}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 722:
|
|
|
|
|
/***/ (function(module) {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Convert array of 16 byte values to UUID string format of the form:
|
|
|
|
|
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
|
|
|
|
*/
|
|
|
|
|
var byteToHex = [];
|
|
|
|
|
for (var i = 0; i < 256; ++i) {
|
|
|
|
|
byteToHex[i] = (i + 0x100).toString(16).substr(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function bytesToUuid(buf, offset) {
|
|
|
|
|
var i = offset || 0;
|
|
|
|
|
var bth = byteToHex;
|
|
|
|
|
// join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
|
|
|
|
|
return ([bth[buf[i++]], bth[buf[i++]],
|
|
|
|
|
bth[buf[i++]], bth[buf[i++]], '-',
|
|
|
|
|
bth[buf[i++]], bth[buf[i++]], '-',
|
|
|
|
|
bth[buf[i++]], bth[buf[i++]], '-',
|
|
|
|
|
bth[buf[i++]], bth[buf[i++]], '-',
|
|
|
|
|
bth[buf[i++]], bth[buf[i++]],
|
|
|
|
|
bth[buf[i++]], bth[buf[i++]],
|
|
|
|
|
bth[buf[i++]], bth[buf[i++]]]).join('');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = bytesToUuid;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 729:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
const url = __webpack_require__(835);
|
|
|
|
|
const path = __webpack_require__(622);
|
|
|
|
|
/**
|
|
|
|
|
* creates an url from a request url and optional base url (http://server:8080)
|
|
|
|
|
* @param {string} resource - a fully qualified url or relative path
|
|
|
|
|
* @param {string} baseUrl - an optional baseUrl (http://server:8080)
|
|
|
|
|
* @return {string} - resultant url
|
|
|
|
|
*/
|
|
|
|
|
function getUrl(resource, baseUrl) {
|
|
|
|
|
const pathApi = path.posix || path;
|
|
|
|
|
if (!baseUrl) {
|
|
|
|
|
return resource;
|
|
|
|
|
}
|
|
|
|
|
else if (!resource) {
|
|
|
|
|
return baseUrl;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const base = url.parse(baseUrl);
|
|
|
|
|
const resultantUrl = url.parse(resource);
|
|
|
|
|
// resource (specific per request) elements take priority
|
|
|
|
|
resultantUrl.protocol = resultantUrl.protocol || base.protocol;
|
|
|
|
|
resultantUrl.auth = resultantUrl.auth || base.auth;
|
|
|
|
|
resultantUrl.host = resultantUrl.host || base.host;
|
|
|
|
|
resultantUrl.pathname = pathApi.resolve(base.pathname, resultantUrl.pathname);
|
|
|
|
|
if (!resultantUrl.pathname.endsWith('/') && resource.endsWith('/')) {
|
|
|
|
|
resultantUrl.pathname += '/';
|
|
|
|
|
}
|
|
|
|
|
return url.format(resultantUrl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
exports.getUrl = getUrl;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 747:
|
|
|
|
|
/***/ (function(module) {
|
|
|
|
|
|
|
|
|
|
module.exports = require("fs");
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 826:
|
|
|
|
|
/***/ (function(module, __unusedexports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
var rng = __webpack_require__(139);
|
|
|
|
|
var bytesToUuid = __webpack_require__(722);
|
|
|
|
|
|
|
|
|
|
function v4(options, buf, offset) {
|
|
|
|
|
var i = buf && offset || 0;
|
|
|
|
|
|
|
|
|
|
if (typeof(options) == 'string') {
|
|
|
|
|
buf = options === 'binary' ? new Array(16) : null;
|
|
|
|
|
options = null;
|
|
|
|
|
}
|
|
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
|
|
var rnds = options.random || (options.rng || rng)();
|
|
|
|
|
|
|
|
|
|
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
|
|
|
|
|
rnds[6] = (rnds[6] & 0x0f) | 0x40;
|
|
|
|
|
rnds[8] = (rnds[8] & 0x3f) | 0x80;
|
|
|
|
|
|
|
|
|
|
// Copy bytes to buffer, if provided
|
|
|
|
|
if (buf) {
|
|
|
|
|
for (var ii = 0; ii < 16; ++ii) {
|
|
|
|
|
buf[i + ii] = rnds[ii];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return buf || bytesToUuid(rnds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = v4;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 835:
|
|
|
|
|
/***/ (function(module) {
|
|
|
|
|
|
|
|
|
|
module.exports = require("url");
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 874:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
|
|
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
|
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
|
|
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
|
|
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
|
|
|
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
|
|
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
const url = __webpack_require__(835);
|
|
|
|
|
const http = __webpack_require__(605);
|
|
|
|
|
const https = __webpack_require__(211);
|
|
|
|
|
let fs;
|
|
|
|
|
let tunnel;
|
|
|
|
|
var HttpCodes;
|
|
|
|
|
(function (HttpCodes) {
|
|
|
|
|
HttpCodes[HttpCodes["OK"] = 200] = "OK";
|
|
|
|
|
HttpCodes[HttpCodes["MultipleChoices"] = 300] = "MultipleChoices";
|
|
|
|
|
HttpCodes[HttpCodes["MovedPermanently"] = 301] = "MovedPermanently";
|
|
|
|
|
HttpCodes[HttpCodes["ResourceMoved"] = 302] = "ResourceMoved";
|
|
|
|
|
HttpCodes[HttpCodes["SeeOther"] = 303] = "SeeOther";
|
|
|
|
|
HttpCodes[HttpCodes["NotModified"] = 304] = "NotModified";
|
|
|
|
|
HttpCodes[HttpCodes["UseProxy"] = 305] = "UseProxy";
|
|
|
|
|
HttpCodes[HttpCodes["SwitchProxy"] = 306] = "SwitchProxy";
|
|
|
|
|
HttpCodes[HttpCodes["TemporaryRedirect"] = 307] = "TemporaryRedirect";
|
|
|
|
|
HttpCodes[HttpCodes["PermanentRedirect"] = 308] = "PermanentRedirect";
|
|
|
|
|
HttpCodes[HttpCodes["BadRequest"] = 400] = "BadRequest";
|
|
|
|
|
HttpCodes[HttpCodes["Unauthorized"] = 401] = "Unauthorized";
|
|
|
|
|
HttpCodes[HttpCodes["PaymentRequired"] = 402] = "PaymentRequired";
|
|
|
|
|
HttpCodes[HttpCodes["Forbidden"] = 403] = "Forbidden";
|
|
|
|
|
HttpCodes[HttpCodes["NotFound"] = 404] = "NotFound";
|
|
|
|
|
HttpCodes[HttpCodes["MethodNotAllowed"] = 405] = "MethodNotAllowed";
|
|
|
|
|
HttpCodes[HttpCodes["NotAcceptable"] = 406] = "NotAcceptable";
|
|
|
|
|
HttpCodes[HttpCodes["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired";
|
|
|
|
|
HttpCodes[HttpCodes["RequestTimeout"] = 408] = "RequestTimeout";
|
|
|
|
|
HttpCodes[HttpCodes["Conflict"] = 409] = "Conflict";
|
|
|
|
|
HttpCodes[HttpCodes["Gone"] = 410] = "Gone";
|
|
|
|
|
HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError";
|
|
|
|
|
HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented";
|
|
|
|
|
HttpCodes[HttpCodes["BadGateway"] = 502] = "BadGateway";
|
|
|
|
|
HttpCodes[HttpCodes["ServiceUnavailable"] = 503] = "ServiceUnavailable";
|
|
|
|
|
HttpCodes[HttpCodes["GatewayTimeout"] = 504] = "GatewayTimeout";
|
|
|
|
|
})(HttpCodes = exports.HttpCodes || (exports.HttpCodes = {}));
|
|
|
|
|
const HttpRedirectCodes = [HttpCodes.MovedPermanently, HttpCodes.ResourceMoved, HttpCodes.SeeOther, HttpCodes.TemporaryRedirect, HttpCodes.PermanentRedirect];
|
|
|
|
|
const HttpResponseRetryCodes = [HttpCodes.BadGateway, HttpCodes.ServiceUnavailable, HttpCodes.GatewayTimeout];
|
|
|
|
|
const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD'];
|
|
|
|
|
const ExponentialBackoffCeiling = 10;
|
|
|
|
|
const ExponentialBackoffTimeSlice = 5;
|
|
|
|
|
class HttpClientResponse {
|
|
|
|
|
constructor(message) {
|
|
|
|
|
this.message = message;
|
|
|
|
|
}
|
|
|
|
|
readBody() {
|
|
|
|
|
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
let output = '';
|
|
|
|
|
this.message.on('data', (chunk) => {
|
|
|
|
|
output += chunk;
|
|
|
|
|
});
|
|
|
|
|
this.message.on('end', () => {
|
|
|
|
|
resolve(output);
|
|
|
|
|
});
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
exports.HttpClientResponse = HttpClientResponse;
|
|
|
|
|
function isHttps(requestUrl) {
|
|
|
|
|
let parsedUrl = url.parse(requestUrl);
|
|
|
|
|
return parsedUrl.protocol === 'https:';
|
|
|
|
|
}
|
|
|
|
|
exports.isHttps = isHttps;
|
|
|
|
|
var EnvironmentVariables;
|
|
|
|
|
(function (EnvironmentVariables) {
|
|
|
|
|
EnvironmentVariables["HTTP_PROXY"] = "HTTP_PROXY";
|
|
|
|
|
EnvironmentVariables["HTTPS_PROXY"] = "HTTPS_PROXY";
|
|
|
|
|
})(EnvironmentVariables || (EnvironmentVariables = {}));
|
|
|
|
|
class HttpClient {
|
|
|
|
|
constructor(userAgent, handlers, requestOptions) {
|
|
|
|
|
this._ignoreSslError = false;
|
|
|
|
|
this._allowRedirects = true;
|
|
|
|
|
this._maxRedirects = 50;
|
|
|
|
|
this._allowRetries = false;
|
|
|
|
|
this._maxRetries = 1;
|
|
|
|
|
this._keepAlive = false;
|
|
|
|
|
this._disposed = false;
|
|
|
|
|
this.userAgent = userAgent;
|
|
|
|
|
this.handlers = handlers || [];
|
|
|
|
|
this.requestOptions = requestOptions;
|
|
|
|
|
if (requestOptions) {
|
|
|
|
|
if (requestOptions.ignoreSslError != null) {
|
|
|
|
|
this._ignoreSslError = requestOptions.ignoreSslError;
|
|
|
|
|
}
|
|
|
|
|
this._socketTimeout = requestOptions.socketTimeout;
|
|
|
|
|
this._httpProxy = requestOptions.proxy;
|
|
|
|
|
if (requestOptions.proxy && requestOptions.proxy.proxyBypassHosts) {
|
|
|
|
|
this._httpProxyBypassHosts = [];
|
|
|
|
|
requestOptions.proxy.proxyBypassHosts.forEach(bypass => {
|
|
|
|
|
this._httpProxyBypassHosts.push(new RegExp(bypass, 'i'));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
this._certConfig = requestOptions.cert;
|
|
|
|
|
if (this._certConfig) {
|
|
|
|
|
// If using cert, need fs
|
|
|
|
|
fs = __webpack_require__(747);
|
|
|
|
|
// cache the cert content into memory, so we don't have to read it from disk every time
|
|
|
|
|
if (this._certConfig.caFile && fs.existsSync(this._certConfig.caFile)) {
|
|
|
|
|
this._ca = fs.readFileSync(this._certConfig.caFile, 'utf8');
|
|
|
|
|
}
|
|
|
|
|
if (this._certConfig.certFile && fs.existsSync(this._certConfig.certFile)) {
|
|
|
|
|
this._cert = fs.readFileSync(this._certConfig.certFile, 'utf8');
|
|
|
|
|
}
|
|
|
|
|
if (this._certConfig.keyFile && fs.existsSync(this._certConfig.keyFile)) {
|
|
|
|
|
this._key = fs.readFileSync(this._certConfig.keyFile, 'utf8');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (requestOptions.allowRedirects != null) {
|
|
|
|
|
this._allowRedirects = requestOptions.allowRedirects;
|
|
|
|
|
}
|
|
|
|
|
if (requestOptions.maxRedirects != null) {
|
|
|
|
|
this._maxRedirects = Math.max(requestOptions.maxRedirects, 0);
|
|
|
|
|
}
|
|
|
|
|
if (requestOptions.keepAlive != null) {
|
|
|
|
|
this._keepAlive = requestOptions.keepAlive;
|
|
|
|
|
}
|
|
|
|
|
if (requestOptions.allowRetries != null) {
|
|
|
|
|
this._allowRetries = requestOptions.allowRetries;
|
|
|
|
|
}
|
|
|
|
|
if (requestOptions.maxRetries != null) {
|
|
|
|
|
this._maxRetries = requestOptions.maxRetries;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
options(requestUrl, additionalHeaders) {
|
|
|
|
|
return this.request('OPTIONS', requestUrl, null, additionalHeaders || {});
|
|
|
|
|
}
|
|
|
|
|
get(requestUrl, additionalHeaders) {
|
|
|
|
|
return this.request('GET', requestUrl, null, additionalHeaders || {});
|
|
|
|
|
}
|
|
|
|
|
del(requestUrl, additionalHeaders) {
|
|
|
|
|
return this.request('DELETE', requestUrl, null, additionalHeaders || {});
|
|
|
|
|
}
|
|
|
|
|
post(requestUrl, data, additionalHeaders) {
|
|
|
|
|
return this.request('POST', requestUrl, data, additionalHeaders || {});
|
|
|
|
|
}
|
|
|
|
|
patch(requestUrl, data, additionalHeaders) {
|
|
|
|
|
return this.request('PATCH', requestUrl, data, additionalHeaders || {});
|
|
|
|
|
}
|
|
|
|
|
put(requestUrl, data, additionalHeaders) {
|
|
|
|
|
return this.request('PUT', requestUrl, data, additionalHeaders || {});
|
|
|
|
|
}
|
|
|
|
|
head(requestUrl, additionalHeaders) {
|
|
|
|
|
return this.request('HEAD', requestUrl, null, additionalHeaders || {});
|
|
|
|
|
}
|
|
|
|
|
sendStream(verb, requestUrl, stream, additionalHeaders) {
|
|
|
|
|
return this.request(verb, requestUrl, stream, additionalHeaders);
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* Makes a raw http request.
|
|
|
|
|
* All other methods such as get, post, patch, and request ultimately call this.
|
|
|
|
|
* Prefer get, del, post and patch
|
|
|
|
|
*/
|
|
|
|
|
request(verb, requestUrl, data, headers) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
if (this._disposed) {
|
|
|
|
|
throw new Error("Client has already been disposed.");
|
|
|
|
|
}
|
|
|
|
|
let info = this._prepareRequest(verb, requestUrl, headers);
|
|
|
|
|
// Only perform retries on reads since writes may not be idempotent.
|
|
|
|
|
let maxTries = (this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1) ? this._maxRetries + 1 : 1;
|
|
|
|
|
let numTries = 0;
|
|
|
|
|
let response;
|
|
|
|
|
while (numTries < maxTries) {
|
|
|
|
|
response = yield this.requestRaw(info, data);
|
|
|
|
|
// Check if it's an authentication challenge
|
|
|
|
|
if (response && response.message && response.message.statusCode === HttpCodes.Unauthorized) {
|
|
|
|
|
let authenticationHandler;
|
|
|
|
|
for (let i = 0; i < this.handlers.length; i++) {
|
|
|
|
|
if (this.handlers[i].canHandleAuthentication(response)) {
|
|
|
|
|
authenticationHandler = this.handlers[i];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (authenticationHandler) {
|
|
|
|
|
return authenticationHandler.handleAuthentication(this, info, data);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// We have received an unauthorized response but have no handlers to handle it.
|
|
|
|
|
// Let the response return to the caller.
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let redirectsRemaining = this._maxRedirects;
|
|
|
|
|
while (HttpRedirectCodes.indexOf(response.message.statusCode) != -1
|
|
|
|
|
&& this._allowRedirects
|
|
|
|
|
&& redirectsRemaining > 0) {
|
|
|
|
|
const redirectUrl = response.message.headers["location"];
|
|
|
|
|
if (!redirectUrl) {
|
|
|
|
|
// if there's no location to redirect to, we won't
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// we need to finish reading the response before reassigning response
|
|
|
|
|
// which will leak the open socket.
|
|
|
|
|
yield response.readBody();
|
|
|
|
|
// let's make the request with the new redirectUrl
|
|
|
|
|
info = this._prepareRequest(verb, redirectUrl, headers);
|
|
|
|
|
response = yield this.requestRaw(info, data);
|
|
|
|
|
redirectsRemaining--;
|
|
|
|
|
}
|
|
|
|
|
if (HttpResponseRetryCodes.indexOf(response.message.statusCode) == -1) {
|
|
|
|
|
// If not a retry code, return immediately instead of retrying
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
numTries += 1;
|
|
|
|
|
if (numTries < maxTries) {
|
|
|
|
|
yield response.readBody();
|
|
|
|
|
yield this._performExponentialBackoff(numTries);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return response;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* Needs to be called if keepAlive is set to true in request options.
|
|
|
|
|
*/
|
|
|
|
|
dispose() {
|
|
|
|
|
if (this._agent) {
|
|
|
|
|
this._agent.destroy();
|
|
|
|
|
}
|
|
|
|
|
this._disposed = true;
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* Raw request.
|
|
|
|
|
* @param info
|
|
|
|
|
* @param data
|
|
|
|
|
*/
|
|
|
|
|
requestRaw(info, data) {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
let callbackForResult = function (err, res) {
|
|
|
|
|
if (err) {
|
|
|
|
|
reject(err);
|
|
|
|
|
}
|
|
|
|
|
resolve(res);
|
|
|
|
|
};
|
|
|
|
|
this.requestRawWithCallback(info, data, callbackForResult);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* Raw request with callback.
|
|
|
|
|
* @param info
|
|
|
|
|
* @param data
|
|
|
|
|
* @param onResult
|
|
|
|
|
*/
|
|
|
|
|
requestRawWithCallback(info, data, onResult) {
|
|
|
|
|
let socket;
|
|
|
|
|
let isDataString = typeof (data) === 'string';
|
|
|
|
|
if (typeof (data) === 'string') {
|
|
|
|
|
info.options.headers["Content-Length"] = Buffer.byteLength(data, 'utf8');
|
|
|
|
|
}
|
|
|
|
|
let callbackCalled = false;
|
|
|
|
|
let handleResult = (err, res) => {
|
|
|
|
|
if (!callbackCalled) {
|
|
|
|
|
callbackCalled = true;
|
|
|
|
|
onResult(err, res);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let req = info.httpModule.request(info.options, (msg) => {
|
|
|
|
|
let res = new HttpClientResponse(msg);
|
|
|
|
|
handleResult(null, res);
|
|
|
|
|
});
|
|
|
|
|
req.on('socket', (sock) => {
|
|
|
|
|
socket = sock;
|
|
|
|
|
});
|
|
|
|
|
// If we ever get disconnected, we want the socket to timeout eventually
|
|
|
|
|
req.setTimeout(this._socketTimeout || 3 * 60000, () => {
|
|
|
|
|
if (socket) {
|
|
|
|
|
socket.end();
|
|
|
|
|
}
|
|
|
|
|
handleResult(new Error('Request timeout: ' + info.options.path), null);
|
|
|
|
|
});
|
|
|
|
|
req.on('error', function (err) {
|
|
|
|
|
// err has statusCode property
|
|
|
|
|
// res should have headers
|
|
|
|
|
handleResult(err, null);
|
|
|
|
|
});
|
|
|
|
|
if (data && typeof (data) === 'string') {
|
|
|
|
|
req.write(data, 'utf8');
|
|
|
|
|
}
|
|
|
|
|
if (data && typeof (data) !== 'string') {
|
|
|
|
|
data.on('close', function () {
|
|
|
|
|
req.end();
|
|
|
|
|
});
|
|
|
|
|
data.pipe(req);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
req.end();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_prepareRequest(method, requestUrl, headers) {
|
|
|
|
|
const info = {};
|
|
|
|
|
info.parsedUrl = url.parse(requestUrl);
|
|
|
|
|
const usingSsl = info.parsedUrl.protocol === 'https:';
|
|
|
|
|
info.httpModule = usingSsl ? https : http;
|
|
|
|
|
const defaultPort = usingSsl ? 443 : 80;
|
|
|
|
|
info.options = {};
|
|
|
|
|
info.options.host = info.parsedUrl.hostname;
|
|
|
|
|
info.options.port = info.parsedUrl.port ? parseInt(info.parsedUrl.port) : defaultPort;
|
|
|
|
|
info.options.path = (info.parsedUrl.pathname || '') + (info.parsedUrl.search || '');
|
|
|
|
|
info.options.method = method;
|
|
|
|
|
info.options.headers = this._mergeHeaders(headers);
|
|
|
|
|
info.options.headers["user-agent"] = this.userAgent;
|
|
|
|
|
info.options.agent = this._getAgent(requestUrl);
|
|
|
|
|
// gives handlers an opportunity to participate
|
|
|
|
|
if (this.handlers && !this._isPresigned(requestUrl)) {
|
|
|
|
|
this.handlers.forEach((handler) => {
|
|
|
|
|
handler.prepareRequest(info.options);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return info;
|
|
|
|
|
}
|
|
|
|
|
_isPresigned(requestUrl) {
|
|
|
|
|
if (this.requestOptions && this.requestOptions.presignedUrlPatterns) {
|
|
|
|
|
const patterns = this.requestOptions.presignedUrlPatterns;
|
|
|
|
|
for (let i = 0; i < patterns.length; i++) {
|
|
|
|
|
if (requestUrl.match(patterns[i])) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
_mergeHeaders(headers) {
|
|
|
|
|
const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => (c[k.toLowerCase()] = obj[k], c), {});
|
|
|
|
|
if (this.requestOptions && this.requestOptions.headers) {
|
|
|
|
|
return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers));
|
|
|
|
|
}
|
|
|
|
|
return lowercaseKeys(headers || {});
|
|
|
|
|
}
|
|
|
|
|
_getAgent(requestUrl) {
|
|
|
|
|
let agent;
|
|
|
|
|
let proxy = this._getProxy(requestUrl);
|
|
|
|
|
let useProxy = proxy.proxyUrl && proxy.proxyUrl.hostname && !this._isBypassProxy(requestUrl);
|
|
|
|
|
if (this._keepAlive && useProxy) {
|
|
|
|
|
agent = this._proxyAgent;
|
|
|
|
|
}
|
|
|
|
|
if (this._keepAlive && !useProxy) {
|
|
|
|
|
agent = this._agent;
|
|
|
|
|
}
|
|
|
|
|
// if agent is already assigned use that agent.
|
|
|
|
|
if (!!agent) {
|
|
|
|
|
return agent;
|
|
|
|
|
}
|
|
|
|
|
let parsedUrl = url.parse(requestUrl);
|
|
|
|
|
const usingSsl = parsedUrl.protocol === 'https:';
|
|
|
|
|
let maxSockets = 100;
|
|
|
|
|
if (!!this.requestOptions) {
|
|
|
|
|
maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets;
|
|
|
|
|
}
|
|
|
|
|
if (useProxy) {
|
|
|
|
|
// If using proxy, need tunnel
|
|
|
|
|
if (!tunnel) {
|
|
|
|
|
tunnel = __webpack_require__(413);
|
|
|
|
|
}
|
|
|
|
|
const agentOptions = {
|
|
|
|
|
maxSockets: maxSockets,
|
|
|
|
|
keepAlive: this._keepAlive,
|
|
|
|
|
proxy: {
|
|
|
|
|
proxyAuth: proxy.proxyAuth,
|
|
|
|
|
host: proxy.proxyUrl.hostname,
|
|
|
|
|
port: proxy.proxyUrl.port
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
let tunnelAgent;
|
|
|
|
|
const overHttps = proxy.proxyUrl.protocol === 'https:';
|
|
|
|
|
if (usingSsl) {
|
|
|
|
|
tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp;
|
|
|
|
|
}
|
|
|
|
|
agent = tunnelAgent(agentOptions);
|
|
|
|
|
this._proxyAgent = agent;
|
|
|
|
|
}
|
|
|
|
|
// if reusing agent across request and tunneling agent isn't assigned create a new agent
|
|
|
|
|
if (this._keepAlive && !agent) {
|
|
|
|
|
const options = { keepAlive: this._keepAlive, maxSockets: maxSockets };
|
|
|
|
|
agent = usingSsl ? new https.Agent(options) : new http.Agent(options);
|
|
|
|
|
this._agent = agent;
|
|
|
|
|
}
|
|
|
|
|
// if not using private agent and tunnel agent isn't setup then use global agent
|
|
|
|
|
if (!agent) {
|
|
|
|
|
agent = usingSsl ? https.globalAgent : http.globalAgent;
|
|
|
|
|
}
|
|
|
|
|
if (usingSsl && this._ignoreSslError) {
|
|
|
|
|
// we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process
|
|
|
|
|
// http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options
|
|
|
|
|
// we have to cast it to any and change it directly
|
|
|
|
|
agent.options = Object.assign(agent.options || {}, { rejectUnauthorized: false });
|
|
|
|
|
}
|
|
|
|
|
if (usingSsl && this._certConfig) {
|
|
|
|
|
agent.options = Object.assign(agent.options || {}, { ca: this._ca, cert: this._cert, key: this._key, passphrase: this._certConfig.passphrase });
|
|
|
|
|
}
|
|
|
|
|
return agent;
|
|
|
|
|
}
|
|
|
|
|
_getProxy(requestUrl) {
|
|
|
|
|
const parsedUrl = url.parse(requestUrl);
|
|
|
|
|
let usingSsl = parsedUrl.protocol === 'https:';
|
|
|
|
|
let proxyConfig = this._httpProxy;
|
|
|
|
|
// fallback to http_proxy and https_proxy env
|
|
|
|
|
let https_proxy = process.env[EnvironmentVariables.HTTPS_PROXY];
|
|
|
|
|
let http_proxy = process.env[EnvironmentVariables.HTTP_PROXY];
|
|
|
|
|
if (!proxyConfig) {
|
|
|
|
|
if (https_proxy && usingSsl) {
|
|
|
|
|
proxyConfig = {
|
|
|
|
|
proxyUrl: https_proxy
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
else if (http_proxy) {
|
|
|
|
|
proxyConfig = {
|
|
|
|
|
proxyUrl: http_proxy
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let proxyUrl;
|
|
|
|
|
let proxyAuth;
|
|
|
|
|
if (proxyConfig) {
|
|
|
|
|
if (proxyConfig.proxyUrl.length > 0) {
|
|
|
|
|
proxyUrl = url.parse(proxyConfig.proxyUrl);
|
|
|
|
|
}
|
|
|
|
|
if (proxyConfig.proxyUsername || proxyConfig.proxyPassword) {
|
|
|
|
|
proxyAuth = proxyConfig.proxyUsername + ":" + proxyConfig.proxyPassword;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return { proxyUrl: proxyUrl, proxyAuth: proxyAuth };
|
|
|
|
|
}
|
|
|
|
|
_isBypassProxy(requestUrl) {
|
|
|
|
|
if (!this._httpProxyBypassHosts) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
let bypass = false;
|
|
|
|
|
this._httpProxyBypassHosts.forEach(bypassHost => {
|
|
|
|
|
if (bypassHost.test(requestUrl)) {
|
|
|
|
|
bypass = true;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return bypass;
|
|
|
|
|
}
|
|
|
|
|
_performExponentialBackoff(retryNumber) {
|
|
|
|
|
retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber);
|
|
|
|
|
const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber);
|
|
|
|
|
return new Promise(resolve => setTimeout(() => resolve(), ms));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
exports.HttpClient = HttpClient;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 891:
|
|
|
|
|
/***/ (function(module, exports) {
|
|
|
|
|
|
|
|
|
|
// Underscore.js 1.8.3
|
|
|
|
|
// http://underscorejs.org
|
|
|
|
|
// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
|
|
|
|
// Underscore may be freely distributed under the MIT license.
|
|
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
|
|
|
|
|
|
// Baseline setup
|
|
|
|
|
// --------------
|
|
|
|
|
|
|
|
|
|
// Establish the root object, `window` in the browser, or `exports` on the server.
|
|
|
|
|
var root = this;
|
|
|
|
|
|
|
|
|
|
// Save the previous value of the `_` variable.
|
|
|
|
|
var previousUnderscore = root._;
|
|
|
|
|
|
|
|
|
|
// Save bytes in the minified (but not gzipped) version:
|
|
|
|
|
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
|
|
|
|
|
|
|
|
|
|
// Create quick reference variables for speed access to core prototypes.
|
|
|
|
|
var
|
|
|
|
|
push = ArrayProto.push,
|
|
|
|
|
slice = ArrayProto.slice,
|
|
|
|
|
toString = ObjProto.toString,
|
|
|
|
|
hasOwnProperty = ObjProto.hasOwnProperty;
|
|
|
|
|
|
|
|
|
|
// All **ECMAScript 5** native function implementations that we hope to use
|
|
|
|
|
// are declared here.
|
|
|
|
|
var
|
|
|
|
|
nativeIsArray = Array.isArray,
|
|
|
|
|
nativeKeys = Object.keys,
|
|
|
|
|
nativeBind = FuncProto.bind,
|
|
|
|
|
nativeCreate = Object.create;
|
|
|
|
|
|
|
|
|
|
// Naked function reference for surrogate-prototype-swapping.
|
|
|
|
|
var Ctor = function(){};
|
|
|
|
|
|
|
|
|
|
// Create a safe reference to the Underscore object for use below.
|
|
|
|
|
var _ = function(obj) {
|
|
|
|
|
if (obj instanceof _) return obj;
|
|
|
|
|
if (!(this instanceof _)) return new _(obj);
|
|
|
|
|
this._wrapped = obj;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Export the Underscore object for **Node.js**, with
|
|
|
|
|
// backwards-compatibility for the old `require()` API. If we're in
|
|
|
|
|
// the browser, add `_` as a global object.
|
|
|
|
|
if (true) {
|
|
|
|
|
if ( true && module.exports) {
|
|
|
|
|
exports = module.exports = _;
|
|
|
|
|
}
|
|
|
|
|
exports._ = _;
|
|
|
|
|
} else {}
|
|
|
|
|
|
|
|
|
|
// Current version.
|
|
|
|
|
_.VERSION = '1.8.3';
|
|
|
|
|
|
|
|
|
|
// Internal function that returns an efficient (for current engines) version
|
|
|
|
|
// of the passed-in callback, to be repeatedly applied in other Underscore
|
|
|
|
|
// functions.
|
|
|
|
|
var optimizeCb = function(func, context, argCount) {
|
|
|
|
|
if (context === void 0) return func;
|
|
|
|
|
switch (argCount == null ? 3 : argCount) {
|
|
|
|
|
case 1: return function(value) {
|
|
|
|
|
return func.call(context, value);
|
|
|
|
|
};
|
|
|
|
|
case 2: return function(value, other) {
|
|
|
|
|
return func.call(context, value, other);
|
|
|
|
|
};
|
|
|
|
|
case 3: return function(value, index, collection) {
|
|
|
|
|
return func.call(context, value, index, collection);
|
|
|
|
|
};
|
|
|
|
|
case 4: return function(accumulator, value, index, collection) {
|
|
|
|
|
return func.call(context, accumulator, value, index, collection);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return function() {
|
|
|
|
|
return func.apply(context, arguments);
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// A mostly-internal function to generate callbacks that can be applied
|
|
|
|
|
// to each element in a collection, returning the desired result — either
|
|
|
|
|
// identity, an arbitrary callback, a property matcher, or a property accessor.
|
|
|
|
|
var cb = function(value, context, argCount) {
|
|
|
|
|
if (value == null) return _.identity;
|
|
|
|
|
if (_.isFunction(value)) return optimizeCb(value, context, argCount);
|
|
|
|
|
if (_.isObject(value)) return _.matcher(value);
|
|
|
|
|
return _.property(value);
|
|
|
|
|
};
|
|
|
|
|
_.iteratee = function(value, context) {
|
|
|
|
|
return cb(value, context, Infinity);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// An internal function for creating assigner functions.
|
|
|
|
|
var createAssigner = function(keysFunc, undefinedOnly) {
|
|
|
|
|
return function(obj) {
|
|
|
|
|
var length = arguments.length;
|
|
|
|
|
if (length < 2 || obj == null) return obj;
|
|
|
|
|
for (var index = 1; index < length; index++) {
|
|
|
|
|
var source = arguments[index],
|
|
|
|
|
keys = keysFunc(source),
|
|
|
|
|
l = keys.length;
|
|
|
|
|
for (var i = 0; i < l; i++) {
|
|
|
|
|
var key = keys[i];
|
|
|
|
|
if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return obj;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// An internal function for creating a new object that inherits from another.
|
|
|
|
|
var baseCreate = function(prototype) {
|
|
|
|
|
if (!_.isObject(prototype)) return {};
|
|
|
|
|
if (nativeCreate) return nativeCreate(prototype);
|
|
|
|
|
Ctor.prototype = prototype;
|
|
|
|
|
var result = new Ctor;
|
|
|
|
|
Ctor.prototype = null;
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var property = function(key) {
|
|
|
|
|
return function(obj) {
|
|
|
|
|
return obj == null ? void 0 : obj[key];
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Helper for collection methods to determine whether a collection
|
|
|
|
|
// should be iterated as an array or as an object
|
|
|
|
|
// Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
|
|
|
|
|
// Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
|
|
|
|
|
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
|
|
|
|
|
var getLength = property('length');
|
|
|
|
|
var isArrayLike = function(collection) {
|
|
|
|
|
var length = getLength(collection);
|
|
|
|
|
return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Collection Functions
|
|
|
|
|
// --------------------
|
|
|
|
|
|
|
|
|
|
// The cornerstone, an `each` implementation, aka `forEach`.
|
|
|
|
|
// Handles raw objects in addition to array-likes. Treats all
|
|
|
|
|
// sparse array-likes as if they were dense.
|
|
|
|
|
_.each = _.forEach = function(obj, iteratee, context) {
|
|
|
|
|
iteratee = optimizeCb(iteratee, context);
|
|
|
|
|
var i, length;
|
|
|
|
|
if (isArrayLike(obj)) {
|
|
|
|
|
for (i = 0, length = obj.length; i < length; i++) {
|
|
|
|
|
iteratee(obj[i], i, obj);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
var keys = _.keys(obj);
|
|
|
|
|
for (i = 0, length = keys.length; i < length; i++) {
|
|
|
|
|
iteratee(obj[keys[i]], keys[i], obj);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return obj;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Return the results of applying the iteratee to each element.
|
|
|
|
|
_.map = _.collect = function(obj, iteratee, context) {
|
|
|
|
|
iteratee = cb(iteratee, context);
|
|
|
|
|
var keys = !isArrayLike(obj) && _.keys(obj),
|
|
|
|
|
length = (keys || obj).length,
|
|
|
|
|
results = Array(length);
|
|
|
|
|
for (var index = 0; index < length; index++) {
|
|
|
|
|
var currentKey = keys ? keys[index] : index;
|
|
|
|
|
results[index] = iteratee(obj[currentKey], currentKey, obj);
|
|
|
|
|
}
|
|
|
|
|
return results;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Create a reducing function iterating left or right.
|
|
|
|
|
function createReduce(dir) {
|
|
|
|
|
// Optimized iterator function as using arguments.length
|
|
|
|
|
// in the main function will deoptimize the, see #1991.
|
|
|
|
|
function iterator(obj, iteratee, memo, keys, index, length) {
|
|
|
|
|
for (; index >= 0 && index < length; index += dir) {
|
|
|
|
|
var currentKey = keys ? keys[index] : index;
|
|
|
|
|
memo = iteratee(memo, obj[currentKey], currentKey, obj);
|
|
|
|
|
}
|
|
|
|
|
return memo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return function(obj, iteratee, memo, context) {
|
|
|
|
|
iteratee = optimizeCb(iteratee, context, 4);
|
|
|
|
|
var keys = !isArrayLike(obj) && _.keys(obj),
|
|
|
|
|
length = (keys || obj).length,
|
|
|
|
|
index = dir > 0 ? 0 : length - 1;
|
|
|
|
|
// Determine the initial value if none is provided.
|
|
|
|
|
if (arguments.length < 3) {
|
|
|
|
|
memo = obj[keys ? keys[index] : index];
|
|
|
|
|
index += dir;
|
|
|
|
|
}
|
|
|
|
|
return iterator(obj, iteratee, memo, keys, index, length);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// **Reduce** builds up a single result from a list of values, aka `inject`,
|
|
|
|
|
// or `foldl`.
|
|
|
|
|
_.reduce = _.foldl = _.inject = createReduce(1);
|
|
|
|
|
|
|
|
|
|
// The right-associative version of reduce, also known as `foldr`.
|
|
|
|
|
_.reduceRight = _.foldr = createReduce(-1);
|
|
|
|
|
|
|
|
|
|
// Return the first value which passes a truth test. Aliased as `detect`.
|
|
|
|
|
_.find = _.detect = function(obj, predicate, context) {
|
|
|
|
|
var key;
|
|
|
|
|
if (isArrayLike(obj)) {
|
|
|
|
|
key = _.findIndex(obj, predicate, context);
|
|
|
|
|
} else {
|
|
|
|
|
key = _.findKey(obj, predicate, context);
|
|
|
|
|
}
|
|
|
|
|
if (key !== void 0 && key !== -1) return obj[key];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Return all the elements that pass a truth test.
|
|
|
|
|
// Aliased as `select`.
|
|
|
|
|
_.filter = _.select = function(obj, predicate, context) {
|
|
|
|
|
var results = [];
|
|
|
|
|
predicate = cb(predicate, context);
|
|
|
|
|
_.each(obj, function(value, index, list) {
|
|
|
|
|
if (predicate(value, index, list)) results.push(value);
|
|
|
|
|
});
|
|
|
|
|
return results;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Return all the elements for which a truth test fails.
|
|
|
|
|
_.reject = function(obj, predicate, context) {
|
|
|
|
|
return _.filter(obj, _.negate(cb(predicate)), context);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Determine whether all of the elements match a truth test.
|
|
|
|
|
// Aliased as `all`.
|
|
|
|
|
_.every = _.all = function(obj, predicate, context) {
|
|
|
|
|
predicate = cb(predicate, context);
|
|
|
|
|
var keys = !isArrayLike(obj) && _.keys(obj),
|
|
|
|
|
length = (keys || obj).length;
|
|
|
|
|
for (var index = 0; index < length; index++) {
|
|
|
|
|
var currentKey = keys ? keys[index] : index;
|
|
|
|
|
if (!predicate(obj[currentKey], currentKey, obj)) return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Determine if at least one element in the object matches a truth test.
|
|
|
|
|
// Aliased as `any`.
|
|
|
|
|
_.some = _.any = function(obj, predicate, context) {
|
|
|
|
|
predicate = cb(predicate, context);
|
|
|
|
|
var keys = !isArrayLike(obj) && _.keys(obj),
|
|
|
|
|
length = (keys || obj).length;
|
|
|
|
|
for (var index = 0; index < length; index++) {
|
|
|
|
|
var currentKey = keys ? keys[index] : index;
|
|
|
|
|
if (predicate(obj[currentKey], currentKey, obj)) return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Determine if the array or object contains a given item (using `===`).
|
|
|
|
|
// Aliased as `includes` and `include`.
|
|
|
|
|
_.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
|
|
|
|
|
if (!isArrayLike(obj)) obj = _.values(obj);
|
|
|
|
|
if (typeof fromIndex != 'number' || guard) fromIndex = 0;
|
|
|
|
|
return _.indexOf(obj, item, fromIndex) >= 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Invoke a method (with arguments) on every item in a collection.
|
|
|
|
|
_.invoke = function(obj, method) {
|
|
|
|
|
var args = slice.call(arguments, 2);
|
|
|
|
|
var isFunc = _.isFunction(method);
|
|
|
|
|
return _.map(obj, function(value) {
|
|
|
|
|
var func = isFunc ? method : value[method];
|
|
|
|
|
return func == null ? func : func.apply(value, args);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Convenience version of a common use case of `map`: fetching a property.
|
|
|
|
|
_.pluck = function(obj, key) {
|
|
|
|
|
return _.map(obj, _.property(key));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Convenience version of a common use case of `filter`: selecting only objects
|
|
|
|
|
// containing specific `key:value` pairs.
|
|
|
|
|
_.where = function(obj, attrs) {
|
|
|
|
|
return _.filter(obj, _.matcher(attrs));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Convenience version of a common use case of `find`: getting the first object
|
|
|
|
|
// containing specific `key:value` pairs.
|
|
|
|
|
_.findWhere = function(obj, attrs) {
|
|
|
|
|
return _.find(obj, _.matcher(attrs));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Return the maximum element (or element-based computation).
|
|
|
|
|
_.max = function(obj, iteratee, context) {
|
|
|
|
|
var result = -Infinity, lastComputed = -Infinity,
|
|
|
|
|
value, computed;
|
|
|
|
|
if (iteratee == null && obj != null) {
|
|
|
|
|
obj = isArrayLike(obj) ? obj : _.values(obj);
|
|
|
|
|
for (var i = 0, length = obj.length; i < length; i++) {
|
|
|
|
|
value = obj[i];
|
|
|
|
|
if (value > result) {
|
|
|
|
|
result = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
iteratee = cb(iteratee, context);
|
|
|
|
|
_.each(obj, function(value, index, list) {
|
|
|
|
|
computed = iteratee(value, index, list);
|
|
|
|
|
if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
|
|
|
|
|
result = value;
|
|
|
|
|
lastComputed = computed;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Return the minimum element (or element-based computation).
|
|
|
|
|
_.min = function(obj, iteratee, context) {
|
|
|
|
|
var result = Infinity, lastComputed = Infinity,
|
|
|
|
|
value, computed;
|
|
|
|
|
if (iteratee == null && obj != null) {
|
|
|
|
|
obj = isArrayLike(obj) ? obj : _.values(obj);
|
|
|
|
|
for (var i = 0, length = obj.length; i < length; i++) {
|
|
|
|
|
value = obj[i];
|
|
|
|
|
if (value < result) {
|
|
|
|
|
result = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
iteratee = cb(iteratee, context);
|
|
|
|
|
_.each(obj, function(value, index, list) {
|
|
|
|
|
computed = iteratee(value, index, list);
|
|
|
|
|
if (computed < lastComputed || computed === Infinity && result === Infinity) {
|
|
|
|
|
result = value;
|
|
|
|
|
lastComputed = computed;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Shuffle a collection, using the modern version of the
|
|
|
|
|
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
|
|
|
|
|
_.shuffle = function(obj) {
|
|
|
|
|
var set = isArrayLike(obj) ? obj : _.values(obj);
|
|
|
|
|
var length = set.length;
|
|
|
|
|
var shuffled = Array(length);
|
|
|
|
|
for (var index = 0, rand; index < length; index++) {
|
|
|
|
|
rand = _.random(0, index);
|
|
|
|
|
if (rand !== index) shuffled[index] = shuffled[rand];
|
|
|
|
|
shuffled[rand] = set[index];
|
|
|
|
|
}
|
|
|
|
|
return shuffled;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Sample **n** random values from a collection.
|
|
|
|
|
// If **n** is not specified, returns a single random element.
|
|
|
|
|
// The internal `guard` argument allows it to work with `map`.
|
|
|
|
|
_.sample = function(obj, n, guard) {
|
|
|
|
|
if (n == null || guard) {
|
|
|
|
|
if (!isArrayLike(obj)) obj = _.values(obj);
|
|
|
|
|
return obj[_.random(obj.length - 1)];
|
|
|
|
|
}
|
|
|
|
|
return _.shuffle(obj).slice(0, Math.max(0, n));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Sort the object's values by a criterion produced by an iteratee.
|
|
|
|
|
_.sortBy = function(obj, iteratee, context) {
|
|
|
|
|
iteratee = cb(iteratee, context);
|
|
|
|
|
return _.pluck(_.map(obj, function(value, index, list) {
|
|
|
|
|
return {
|
|
|
|
|
value: value,
|
|
|
|
|
index: index,
|
|
|
|
|
criteria: iteratee(value, index, list)
|
|
|
|
|
};
|
|
|
|
|
}).sort(function(left, right) {
|
|
|
|
|
var a = left.criteria;
|
|
|
|
|
var b = right.criteria;
|
|
|
|
|
if (a !== b) {
|
|
|
|
|
if (a > b || a === void 0) return 1;
|
|
|
|
|
if (a < b || b === void 0) return -1;
|
|
|
|
|
}
|
|
|
|
|
return left.index - right.index;
|
|
|
|
|
}), 'value');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// An internal function used for aggregate "group by" operations.
|
|
|
|
|
var group = function(behavior) {
|
|
|
|
|
return function(obj, iteratee, context) {
|
|
|
|
|
var result = {};
|
|
|
|
|
iteratee = cb(iteratee, context);
|
|
|
|
|
_.each(obj, function(value, index) {
|
|
|
|
|
var key = iteratee(value, index, obj);
|
|
|
|
|
behavior(result, value, key);
|
|
|
|
|
});
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Groups the object's values by a criterion. Pass either a string attribute
|
|
|
|
|
// to group by, or a function that returns the criterion.
|
|
|
|
|
_.groupBy = group(function(result, value, key) {
|
|
|
|
|
if (_.has(result, key)) result[key].push(value); else result[key] = [value];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Indexes the object's values by a criterion, similar to `groupBy`, but for
|
|
|
|
|
// when you know that your index values will be unique.
|
|
|
|
|
_.indexBy = group(function(result, value, key) {
|
|
|
|
|
result[key] = value;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Counts instances of an object that group by a certain criterion. Pass
|
|
|
|
|
// either a string attribute to count by, or a function that returns the
|
|
|
|
|
// criterion.
|
|
|
|
|
_.countBy = group(function(result, value, key) {
|
|
|
|
|
if (_.has(result, key)) result[key]++; else result[key] = 1;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Safely create a real, live array from anything iterable.
|
|
|
|
|
_.toArray = function(obj) {
|
|
|
|
|
if (!obj) return [];
|
|
|
|
|
if (_.isArray(obj)) return slice.call(obj);
|
|
|
|
|
if (isArrayLike(obj)) return _.map(obj, _.identity);
|
|
|
|
|
return _.values(obj);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Return the number of elements in an object.
|
|
|
|
|
_.size = function(obj) {
|
|
|
|
|
if (obj == null) return 0;
|
|
|
|
|
return isArrayLike(obj) ? obj.length : _.keys(obj).length;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Split a collection into two arrays: one whose elements all satisfy the given
|
|
|
|
|
// predicate, and one whose elements all do not satisfy the predicate.
|
|
|
|
|
_.partition = function(obj, predicate, context) {
|
|
|
|
|
predicate = cb(predicate, context);
|
|
|
|
|
var pass = [], fail = [];
|
|
|
|
|
_.each(obj, function(value, key, obj) {
|
|
|
|
|
(predicate(value, key, obj) ? pass : fail).push(value);
|
|
|
|
|
});
|
|
|
|
|
return [pass, fail];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Array Functions
|
|
|
|
|
// ---------------
|
|
|
|
|
|
|
|
|
|
// Get the first element of an array. Passing **n** will return the first N
|
|
|
|
|
// values in the array. Aliased as `head` and `take`. The **guard** check
|
|
|
|
|
// allows it to work with `_.map`.
|
|
|
|
|
_.first = _.head = _.take = function(array, n, guard) {
|
|
|
|
|
if (array == null) return void 0;
|
|
|
|
|
if (n == null || guard) return array[0];
|
|
|
|
|
return _.initial(array, array.length - n);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Returns everything but the last entry of the array. Especially useful on
|
|
|
|
|
// the arguments object. Passing **n** will return all the values in
|
|
|
|
|
// the array, excluding the last N.
|
|
|
|
|
_.initial = function(array, n, guard) {
|
|
|
|
|
return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Get the last element of an array. Passing **n** will return the last N
|
|
|
|
|
// values in the array.
|
|
|
|
|
_.last = function(array, n, guard) {
|
|
|
|
|
if (array == null) return void 0;
|
|
|
|
|
if (n == null || guard) return array[array.length - 1];
|
|
|
|
|
return _.rest(array, Math.max(0, array.length - n));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
|
|
|
|
|
// Especially useful on the arguments object. Passing an **n** will return
|
|
|
|
|
// the rest N values in the array.
|
|
|
|
|
_.rest = _.tail = _.drop = function(array, n, guard) {
|
|
|
|
|
return slice.call(array, n == null || guard ? 1 : n);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Trim out all falsy values from an array.
|
|
|
|
|
_.compact = function(array) {
|
|
|
|
|
return _.filter(array, _.identity);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Internal implementation of a recursive `flatten` function.
|
|
|
|
|
var flatten = function(input, shallow, strict, startIndex) {
|
|
|
|
|
var output = [], idx = 0;
|
|
|
|
|
for (var i = startIndex || 0, length = getLength(input); i < length; i++) {
|
|
|
|
|
var value = input[i];
|
|
|
|
|
if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
|
|
|
|
|
//flatten current level of array or arguments object
|
|
|
|
|
if (!shallow) value = flatten(value, shallow, strict);
|
|
|
|
|
var j = 0, len = value.length;
|
|
|
|
|
output.length += len;
|
|
|
|
|
while (j < len) {
|
|
|
|
|
output[idx++] = value[j++];
|
|
|
|
|
}
|
|
|
|
|
} else if (!strict) {
|
|
|
|
|
output[idx++] = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return output;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Flatten out an array, either recursively (by default), or just one level.
|
|
|
|
|
_.flatten = function(array, shallow) {
|
|
|
|
|
return flatten(array, shallow, false);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Return a version of the array that does not contain the specified value(s).
|
|
|
|
|
_.without = function(array) {
|
|
|
|
|
return _.difference(array, slice.call(arguments, 1));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Produce a duplicate-free version of the array. If the array has already
|
|
|
|
|
// been sorted, you have the option of using a faster algorithm.
|
|
|
|
|
// Aliased as `unique`.
|
|
|
|
|
_.uniq = _.unique = function(array, isSorted, iteratee, context) {
|
|
|
|
|
if (!_.isBoolean(isSorted)) {
|
|
|
|
|
context = iteratee;
|
|
|
|
|
iteratee = isSorted;
|
|
|
|
|
isSorted = false;
|
|
|
|
|
}
|
|
|
|
|
if (iteratee != null) iteratee = cb(iteratee, context);
|
|
|
|
|
var result = [];
|
|
|
|
|
var seen = [];
|
|
|
|
|
for (var i = 0, length = getLength(array); i < length; i++) {
|
|
|
|
|
var value = array[i],
|
|
|
|
|
computed = iteratee ? iteratee(value, i, array) : value;
|
|
|
|
|
if (isSorted) {
|
|
|
|
|
if (!i || seen !== computed) result.push(value);
|
|
|
|
|
seen = computed;
|
|
|
|
|
} else if (iteratee) {
|
|
|
|
|
if (!_.contains(seen, computed)) {
|
|
|
|
|
seen.push(computed);
|
|
|
|
|
result.push(value);
|
|
|
|
|
}
|
|
|
|
|
} else if (!_.contains(result, value)) {
|
|
|
|
|
result.push(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Produce an array that contains the union: each distinct element from all of
|
|
|
|
|
// the passed-in arrays.
|
|
|
|
|
_.union = function() {
|
|
|
|
|
return _.uniq(flatten(arguments, true, true));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Produce an array that contains every item shared between all the
|
|
|
|
|
// passed-in arrays.
|
|
|
|
|
_.intersection = function(array) {
|
|
|
|
|
var result = [];
|
|
|
|
|
var argsLength = arguments.length;
|
|
|
|
|
for (var i = 0, length = getLength(array); i < length; i++) {
|
|
|
|
|
var item = array[i];
|
|
|
|
|
if (_.contains(result, item)) continue;
|
|
|
|
|
for (var j = 1; j < argsLength; j++) {
|
|
|
|
|
if (!_.contains(arguments[j], item)) break;
|
|
|
|
|
}
|
|
|
|
|
if (j === argsLength) result.push(item);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Take the difference between one array and a number of other arrays.
|
|
|
|
|
// Only the elements present in just the first array will remain.
|
|
|
|
|
_.difference = function(array) {
|
|
|
|
|
var rest = flatten(arguments, true, true, 1);
|
|
|
|
|
return _.filter(array, function(value){
|
|
|
|
|
return !_.contains(rest, value);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Zip together multiple lists into a single array -- elements that share
|
|
|
|
|
// an index go together.
|
|
|
|
|
_.zip = function() {
|
|
|
|
|
return _.unzip(arguments);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Complement of _.zip. Unzip accepts an array of arrays and groups
|
|
|
|
|
// each array's elements on shared indices
|
|
|
|
|
_.unzip = function(array) {
|
|
|
|
|
var length = array && _.max(array, getLength).length || 0;
|
|
|
|
|
var result = Array(length);
|
|
|
|
|
|
|
|
|
|
for (var index = 0; index < length; index++) {
|
|
|
|
|
result[index] = _.pluck(array, index);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Converts lists into objects. Pass either a single array of `[key, value]`
|
|
|
|
|
// pairs, or two parallel arrays of the same length -- one of keys, and one of
|
|
|
|
|
// the corresponding values.
|
|
|
|
|
_.object = function(list, values) {
|
|
|
|
|
var result = {};
|
|
|
|
|
for (var i = 0, length = getLength(list); i < length; i++) {
|
|
|
|
|
if (values) {
|
|
|
|
|
result[list[i]] = values[i];
|
|
|
|
|
} else {
|
|
|
|
|
result[list[i][0]] = list[i][1];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Generator function to create the findIndex and findLastIndex functions
|
|
|
|
|
function createPredicateIndexFinder(dir) {
|
|
|
|
|
return function(array, predicate, context) {
|
|
|
|
|
predicate = cb(predicate, context);
|
|
|
|
|
var length = getLength(array);
|
|
|
|
|
var index = dir > 0 ? 0 : length - 1;
|
|
|
|
|
for (; index >= 0 && index < length; index += dir) {
|
|
|
|
|
if (predicate(array[index], index, array)) return index;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns the first index on an array-like that passes a predicate test
|
|
|
|
|
_.findIndex = createPredicateIndexFinder(1);
|
|
|
|
|
_.findLastIndex = createPredicateIndexFinder(-1);
|
|
|
|
|
|
|
|
|
|
// Use a comparator function to figure out the smallest index at which
|
|
|
|
|
// an object should be inserted so as to maintain order. Uses binary search.
|
|
|
|
|
_.sortedIndex = function(array, obj, iteratee, context) {
|
|
|
|
|
iteratee = cb(iteratee, context, 1);
|
|
|
|
|
var value = iteratee(obj);
|
|
|
|
|
var low = 0, high = getLength(array);
|
|
|
|
|
while (low < high) {
|
|
|
|
|
var mid = Math.floor((low + high) / 2);
|
|
|
|
|
if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
|
|
|
|
|
}
|
|
|
|
|
return low;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Generator function to create the indexOf and lastIndexOf functions
|
|
|
|
|
function createIndexFinder(dir, predicateFind, sortedIndex) {
|
|
|
|
|
return function(array, item, idx) {
|
|
|
|
|
var i = 0, length = getLength(array);
|
|
|
|
|
if (typeof idx == 'number') {
|
|
|
|
|
if (dir > 0) {
|
|
|
|
|
i = idx >= 0 ? idx : Math.max(idx + length, i);
|
|
|
|
|
} else {
|
|
|
|
|
length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
|
|
|
|
|
}
|
|
|
|
|
} else if (sortedIndex && idx && length) {
|
|
|
|
|
idx = sortedIndex(array, item);
|
|
|
|
|
return array[idx] === item ? idx : -1;
|
|
|
|
|
}
|
|
|
|
|
if (item !== item) {
|
|
|
|
|
idx = predicateFind(slice.call(array, i, length), _.isNaN);
|
|
|
|
|
return idx >= 0 ? idx + i : -1;
|
|
|
|
|
}
|
|
|
|
|
for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
|
|
|
|
|
if (array[idx] === item) return idx;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return the position of the first occurrence of an item in an array,
|
|
|
|
|
// or -1 if the item is not included in the array.
|
|
|
|
|
// If the array is large and already in sort order, pass `true`
|
|
|
|
|
// for **isSorted** to use binary search.
|
|
|
|
|
_.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
|
|
|
|
|
_.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
|
|
|
|
|
|
|
|
|
|
// Generate an integer Array containing an arithmetic progression. A port of
|
|
|
|
|
// the native Python `range()` function. See
|
|
|
|
|
// [the Python documentation](http://docs.python.org/library/functions.html#range).
|
|
|
|
|
_.range = function(start, stop, step) {
|
|
|
|
|
if (stop == null) {
|
|
|
|
|
stop = start || 0;
|
|
|
|
|
start = 0;
|
|
|
|
|
}
|
|
|
|
|
step = step || 1;
|
|
|
|
|
|
|
|
|
|
var length = Math.max(Math.ceil((stop - start) / step), 0);
|
|
|
|
|
var range = Array(length);
|
|
|
|
|
|
|
|
|
|
for (var idx = 0; idx < length; idx++, start += step) {
|
|
|
|
|
range[idx] = start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return range;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Function (ahem) Functions
|
|
|
|
|
// ------------------
|
|
|
|
|
|
|
|
|
|
// Determines whether to execute a function as a constructor
|
|
|
|
|
// or a normal function with the provided arguments
|
|
|
|
|
var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
|
|
|
|
|
if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
|
|
|
|
|
var self = baseCreate(sourceFunc.prototype);
|
|
|
|
|
var result = sourceFunc.apply(self, args);
|
|
|
|
|
if (_.isObject(result)) return result;
|
|
|
|
|
return self;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Create a function bound to a given object (assigning `this`, and arguments,
|
|
|
|
|
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
|
|
|
|
|
// available.
|
|
|
|
|
_.bind = function(func, context) {
|
|
|
|
|
if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
|
|
|
|
|
if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
|
|
|
|
|
var args = slice.call(arguments, 2);
|
|
|
|
|
var bound = function() {
|
|
|
|
|
return executeBound(func, bound, context, this, args.concat(slice.call(arguments)));
|
|
|
|
|
};
|
|
|
|
|
return bound;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Partially apply a function by creating a version that has had some of its
|
|
|
|
|
// arguments pre-filled, without changing its dynamic `this` context. _ acts
|
|
|
|
|
// as a placeholder, allowing any combination of arguments to be pre-filled.
|
|
|
|
|
_.partial = function(func) {
|
|
|
|
|
var boundArgs = slice.call(arguments, 1);
|
|
|
|
|
var bound = function() {
|
|
|
|
|
var position = 0, length = boundArgs.length;
|
|
|
|
|
var args = Array(length);
|
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
|
args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i];
|
|
|
|
|
}
|
|
|
|
|
while (position < arguments.length) args.push(arguments[position++]);
|
|
|
|
|
return executeBound(func, bound, this, this, args);
|
|
|
|
|
};
|
|
|
|
|
return bound;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Bind a number of an object's methods to that object. Remaining arguments
|
|
|
|
|
// are the method names to be bound. Useful for ensuring that all callbacks
|
|
|
|
|
// defined on an object belong to it.
|
|
|
|
|
_.bindAll = function(obj) {
|
|
|
|
|
var i, length = arguments.length, key;
|
|
|
|
|
if (length <= 1) throw new Error('bindAll must be passed function names');
|
|
|
|
|
for (i = 1; i < length; i++) {
|
|
|
|
|
key = arguments[i];
|
|
|
|
|
obj[key] = _.bind(obj[key], obj);
|
|
|
|
|
}
|
|
|
|
|
return obj;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Memoize an expensive function by storing its results.
|
|
|
|
|
_.memoize = function(func, hasher) {
|
|
|
|
|
var memoize = function(key) {
|
|
|
|
|
var cache = memoize.cache;
|
|
|
|
|
var address = '' + (hasher ? hasher.apply(this, arguments) : key);
|
|
|
|
|
if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
|
|
|
|
|
return cache[address];
|
|
|
|
|
};
|
|
|
|
|
memoize.cache = {};
|
|
|
|
|
return memoize;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Delays a function for the given number of milliseconds, and then calls
|
|
|
|
|
// it with the arguments supplied.
|
|
|
|
|
_.delay = function(func, wait) {
|
|
|
|
|
var args = slice.call(arguments, 2);
|
|
|
|
|
return setTimeout(function(){
|
|
|
|
|
return func.apply(null, args);
|
|
|
|
|
}, wait);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Defers a function, scheduling it to run after the current call stack has
|
|
|
|
|
// cleared.
|
|
|
|
|
_.defer = _.partial(_.delay, _, 1);
|
|
|
|
|
|
|
|
|
|
// Returns a function, that, when invoked, will only be triggered at most once
|
|
|
|
|
// during a given window of time. Normally, the throttled function will run
|
|
|
|
|
// as much as it can, without ever going more than once per `wait` duration;
|
|
|
|
|
// but if you'd like to disable the execution on the leading edge, pass
|
|
|
|
|
// `{leading: false}`. To disable execution on the trailing edge, ditto.
|
|
|
|
|
_.throttle = function(func, wait, options) {
|
|
|
|
|
var context, args, result;
|
|
|
|
|
var timeout = null;
|
|
|
|
|
var previous = 0;
|
|
|
|
|
if (!options) options = {};
|
|
|
|
|
var later = function() {
|
|
|
|
|
previous = options.leading === false ? 0 : _.now();
|
|
|
|
|
timeout = null;
|
|
|
|
|
result = func.apply(context, args);
|
|
|
|
|
if (!timeout) context = args = null;
|
|
|
|
|
};
|
|
|
|
|
return function() {
|
|
|
|
|
var now = _.now();
|
|
|
|
|
if (!previous && options.leading === false) previous = now;
|
|
|
|
|
var remaining = wait - (now - previous);
|
|
|
|
|
context = this;
|
|
|
|
|
args = arguments;
|
|
|
|
|
if (remaining <= 0 || remaining > wait) {
|
|
|
|
|
if (timeout) {
|
|
|
|
|
clearTimeout(timeout);
|
|
|
|
|
timeout = null;
|
|
|
|
|
}
|
|
|
|
|
previous = now;
|
|
|
|
|
result = func.apply(context, args);
|
|
|
|
|
if (!timeout) context = args = null;
|
|
|
|
|
} else if (!timeout && options.trailing !== false) {
|
|
|
|
|
timeout = setTimeout(later, remaining);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Returns a function, that, as long as it continues to be invoked, will not
|
|
|
|
|
// be triggered. The function will be called after it stops being called for
|
|
|
|
|
// N milliseconds. If `immediate` is passed, trigger the function on the
|
|
|
|
|
// leading edge, instead of the trailing.
|
|
|
|
|
_.debounce = function(func, wait, immediate) {
|
|
|
|
|
var timeout, args, context, timestamp, result;
|
|
|
|
|
|
|
|
|
|
var later = function() {
|
|
|
|
|
var last = _.now() - timestamp;
|
|
|
|
|
|
|
|
|
|
if (last < wait && last >= 0) {
|
|
|
|
|
timeout = setTimeout(later, wait - last);
|
|
|
|
|
} else {
|
|
|
|
|
timeout = null;
|
|
|
|
|
if (!immediate) {
|
|
|
|
|
result = func.apply(context, args);
|
|
|
|
|
if (!timeout) context = args = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return function() {
|
|
|
|
|
context = this;
|
|
|
|
|
args = arguments;
|
|
|
|
|
timestamp = _.now();
|
|
|
|
|
var callNow = immediate && !timeout;
|
|
|
|
|
if (!timeout) timeout = setTimeout(later, wait);
|
|
|
|
|
if (callNow) {
|
|
|
|
|
result = func.apply(context, args);
|
|
|
|
|
context = args = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Returns the first function passed as an argument to the second,
|
|
|
|
|
// allowing you to adjust arguments, run code before and after, and
|
|
|
|
|
// conditionally execute the original function.
|
|
|
|
|
_.wrap = function(func, wrapper) {
|
|
|
|
|
return _.partial(wrapper, func);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Returns a negated version of the passed-in predicate.
|
|
|
|
|
_.negate = function(predicate) {
|
|
|
|
|
return function() {
|
|
|
|
|
return !predicate.apply(this, arguments);
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Returns a function that is the composition of a list of functions, each
|
|
|
|
|
// consuming the return value of the function that follows.
|
|
|
|
|
_.compose = function() {
|
|
|
|
|
var args = arguments;
|
|
|
|
|
var start = args.length - 1;
|
|
|
|
|
return function() {
|
|
|
|
|
var i = start;
|
|
|
|
|
var result = args[start].apply(this, arguments);
|
|
|
|
|
while (i--) result = args[i].call(this, result);
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Returns a function that will only be executed on and after the Nth call.
|
|
|
|
|
_.after = function(times, func) {
|
|
|
|
|
return function() {
|
|
|
|
|
if (--times < 1) {
|
|
|
|
|
return func.apply(this, arguments);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Returns a function that will only be executed up to (but not including) the Nth call.
|
|
|
|
|
_.before = function(times, func) {
|
|
|
|
|
var memo;
|
|
|
|
|
return function() {
|
|
|
|
|
if (--times > 0) {
|
|
|
|
|
memo = func.apply(this, arguments);
|
|
|
|
|
}
|
|
|
|
|
if (times <= 1) func = null;
|
|
|
|
|
return memo;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Returns a function that will be executed at most one time, no matter how
|
|
|
|
|
// often you call it. Useful for lazy initialization.
|
|
|
|
|
_.once = _.partial(_.before, 2);
|
|
|
|
|
|
|
|
|
|
// Object Functions
|
|
|
|
|
// ----------------
|
|
|
|
|
|
|
|
|
|
// Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
|
|
|
|
|
var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
|
|
|
|
|
var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
|
|
|
|
|
'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
|
|
|
|
|
|
|
|
|
|
function collectNonEnumProps(obj, keys) {
|
|
|
|
|
var nonEnumIdx = nonEnumerableProps.length;
|
|
|
|
|
var constructor = obj.constructor;
|
|
|
|
|
var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto;
|
|
|
|
|
|
|
|
|
|
// Constructor is a special case.
|
|
|
|
|
var prop = 'constructor';
|
|
|
|
|
if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
|
|
|
|
|
|
|
|
|
|
while (nonEnumIdx--) {
|
|
|
|
|
prop = nonEnumerableProps[nonEnumIdx];
|
|
|
|
|
if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
|
|
|
|
|
keys.push(prop);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Retrieve the names of an object's own properties.
|
|
|
|
|
// Delegates to **ECMAScript 5**'s native `Object.keys`
|
|
|
|
|
_.keys = function(obj) {
|
|
|
|
|
if (!_.isObject(obj)) return [];
|
|
|
|
|
if (nativeKeys) return nativeKeys(obj);
|
|
|
|
|
var keys = [];
|
|
|
|
|
for (var key in obj) if (_.has(obj, key)) keys.push(key);
|
|
|
|
|
// Ahem, IE < 9.
|
|
|
|
|
if (hasEnumBug) collectNonEnumProps(obj, keys);
|
|
|
|
|
return keys;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Retrieve all the property names of an object.
|
|
|
|
|
_.allKeys = function(obj) {
|
|
|
|
|
if (!_.isObject(obj)) return [];
|
|
|
|
|
var keys = [];
|
|
|
|
|
for (var key in obj) keys.push(key);
|
|
|
|
|
// Ahem, IE < 9.
|
|
|
|
|
if (hasEnumBug) collectNonEnumProps(obj, keys);
|
|
|
|
|
return keys;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Retrieve the values of an object's properties.
|
|
|
|
|
_.values = function(obj) {
|
|
|
|
|
var keys = _.keys(obj);
|
|
|
|
|
var length = keys.length;
|
|
|
|
|
var values = Array(length);
|
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
|
values[i] = obj[keys[i]];
|
|
|
|
|
}
|
|
|
|
|
return values;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Returns the results of applying the iteratee to each element of the object
|
|
|
|
|
// In contrast to _.map it returns an object
|
|
|
|
|
_.mapObject = function(obj, iteratee, context) {
|
|
|
|
|
iteratee = cb(iteratee, context);
|
|
|
|
|
var keys = _.keys(obj),
|
|
|
|
|
length = keys.length,
|
|
|
|
|
results = {},
|
|
|
|
|
currentKey;
|
|
|
|
|
for (var index = 0; index < length; index++) {
|
|
|
|
|
currentKey = keys[index];
|
|
|
|
|
results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
|
|
|
|
|
}
|
|
|
|
|
return results;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Convert an object into a list of `[key, value]` pairs.
|
|
|
|
|
_.pairs = function(obj) {
|
|
|
|
|
var keys = _.keys(obj);
|
|
|
|
|
var length = keys.length;
|
|
|
|
|
var pairs = Array(length);
|
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
|
pairs[i] = [keys[i], obj[keys[i]]];
|
|
|
|
|
}
|
|
|
|
|
return pairs;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Invert the keys and values of an object. The values must be serializable.
|
|
|
|
|
_.invert = function(obj) {
|
|
|
|
|
var result = {};
|
|
|
|
|
var keys = _.keys(obj);
|
|
|
|
|
for (var i = 0, length = keys.length; i < length; i++) {
|
|
|
|
|
result[obj[keys[i]]] = keys[i];
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Return a sorted list of the function names available on the object.
|
|
|
|
|
// Aliased as `methods`
|
|
|
|
|
_.functions = _.methods = function(obj) {
|
|
|
|
|
var names = [];
|
|
|
|
|
for (var key in obj) {
|
|
|
|
|
if (_.isFunction(obj[key])) names.push(key);
|
|
|
|
|
}
|
|
|
|
|
return names.sort();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Extend a given object with all the properties in passed-in object(s).
|
|
|
|
|
_.extend = createAssigner(_.allKeys);
|
|
|
|
|
|
|
|
|
|
// Assigns a given object with all the own properties in the passed-in object(s)
|
|
|
|
|
// (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
|
|
|
|
|
_.extendOwn = _.assign = createAssigner(_.keys);
|
|
|
|
|
|
|
|
|
|
// Returns the first key on an object that passes a predicate test
|
|
|
|
|
_.findKey = function(obj, predicate, context) {
|
|
|
|
|
predicate = cb(predicate, context);
|
|
|
|
|
var keys = _.keys(obj), key;
|
|
|
|
|
for (var i = 0, length = keys.length; i < length; i++) {
|
|
|
|
|
key = keys[i];
|
|
|
|
|
if (predicate(obj[key], key, obj)) return key;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Return a copy of the object only containing the whitelisted properties.
|
|
|
|
|
_.pick = function(object, oiteratee, context) {
|
|
|
|
|
var result = {}, obj = object, iteratee, keys;
|
|
|
|
|
if (obj == null) return result;
|
|
|
|
|
if (_.isFunction(oiteratee)) {
|
|
|
|
|
keys = _.allKeys(obj);
|
|
|
|
|
iteratee = optimizeCb(oiteratee, context);
|
|
|
|
|
} else {
|
|
|
|
|
keys = flatten(arguments, false, false, 1);
|
|
|
|
|
iteratee = function(value, key, obj) { return key in obj; };
|
|
|
|
|
obj = Object(obj);
|
|
|
|
|
}
|
|
|
|
|
for (var i = 0, length = keys.length; i < length; i++) {
|
|
|
|
|
var key = keys[i];
|
|
|
|
|
var value = obj[key];
|
|
|
|
|
if (iteratee(value, key, obj)) result[key] = value;
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Return a copy of the object without the blacklisted properties.
|
|
|
|
|
_.omit = function(obj, iteratee, context) {
|
|
|
|
|
if (_.isFunction(iteratee)) {
|
|
|
|
|
iteratee = _.negate(iteratee);
|
|
|
|
|
} else {
|
|
|
|
|
var keys = _.map(flatten(arguments, false, false, 1), String);
|
|
|
|
|
iteratee = function(value, key) {
|
|
|
|
|
return !_.contains(keys, key);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return _.pick(obj, iteratee, context);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Fill in a given object with default properties.
|
|
|
|
|
_.defaults = createAssigner(_.allKeys, true);
|
|
|
|
|
|
|
|
|
|
// Creates an object that inherits from the given prototype object.
|
|
|
|
|
// If additional properties are provided then they will be added to the
|
|
|
|
|
// created object.
|
|
|
|
|
_.create = function(prototype, props) {
|
|
|
|
|
var result = baseCreate(prototype);
|
|
|
|
|
if (props) _.extendOwn(result, props);
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Create a (shallow-cloned) duplicate of an object.
|
|
|
|
|
_.clone = function(obj) {
|
|
|
|
|
if (!_.isObject(obj)) return obj;
|
|
|
|
|
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Invokes interceptor with the obj, and then returns obj.
|
|
|
|
|
// The primary purpose of this method is to "tap into" a method chain, in
|
|
|
|
|
// order to perform operations on intermediate results within the chain.
|
|
|
|
|
_.tap = function(obj, interceptor) {
|
|
|
|
|
interceptor(obj);
|
|
|
|
|
return obj;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Returns whether an object has a given set of `key:value` pairs.
|
|
|
|
|
_.isMatch = function(object, attrs) {
|
|
|
|
|
var keys = _.keys(attrs), length = keys.length;
|
|
|
|
|
if (object == null) return !length;
|
|
|
|
|
var obj = Object(object);
|
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
|
var key = keys[i];
|
|
|
|
|
if (attrs[key] !== obj[key] || !(key in obj)) return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Internal recursive comparison function for `isEqual`.
|
|
|
|
|
var eq = function(a, b, aStack, bStack) {
|
|
|
|
|
// Identical objects are equal. `0 === -0`, but they aren't identical.
|
|
|
|
|
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
|
|
|
|
|
if (a === b) return a !== 0 || 1 / a === 1 / b;
|
|
|
|
|
// A strict comparison is necessary because `null == undefined`.
|
|
|
|
|
if (a == null || b == null) return a === b;
|
|
|
|
|
// Unwrap any wrapped objects.
|
|
|
|
|
if (a instanceof _) a = a._wrapped;
|
|
|
|
|
if (b instanceof _) b = b._wrapped;
|
|
|
|
|
// Compare `[[Class]]` names.
|
|
|
|
|
var className = toString.call(a);
|
|
|
|
|
if (className !== toString.call(b)) return false;
|
|
|
|
|
switch (className) {
|
|
|
|
|
// Strings, numbers, regular expressions, dates, and booleans are compared by value.
|
|
|
|
|
case '[object RegExp]':
|
|
|
|
|
// RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
|
|
|
|
|
case '[object String]':
|
|
|
|
|
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
|
|
|
|
|
// equivalent to `new String("5")`.
|
|
|
|
|
return '' + a === '' + b;
|
|
|
|
|
case '[object Number]':
|
|
|
|
|
// `NaN`s are equivalent, but non-reflexive.
|
|
|
|
|
// Object(NaN) is equivalent to NaN
|
|
|
|
|
if (+a !== +a) return +b !== +b;
|
|
|
|
|
// An `egal` comparison is performed for other numeric values.
|
|
|
|
|
return +a === 0 ? 1 / +a === 1 / b : +a === +b;
|
|
|
|
|
case '[object Date]':
|
|
|
|
|
case '[object Boolean]':
|
|
|
|
|
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
|
|
|
|
|
// millisecond representations. Note that invalid dates with millisecond representations
|
|
|
|
|
// of `NaN` are not equivalent.
|
|
|
|
|
return +a === +b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var areArrays = className === '[object Array]';
|
|
|
|
|
if (!areArrays) {
|
|
|
|
|
if (typeof a != 'object' || typeof b != 'object') return false;
|
|
|
|
|
|
|
|
|
|
// Objects with different constructors are not equivalent, but `Object`s or `Array`s
|
|
|
|
|
// from different frames are.
|
|
|
|
|
var aCtor = a.constructor, bCtor = b.constructor;
|
|
|
|
|
if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
|
|
|
|
|
_.isFunction(bCtor) && bCtor instanceof bCtor)
|
|
|
|
|
&& ('constructor' in a && 'constructor' in b)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Assume equality for cyclic structures. The algorithm for detecting cyclic
|
|
|
|
|
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
|
|
|
|
|
|
|
|
|
|
// Initializing stack of traversed objects.
|
|
|
|
|
// It's done here since we only need them for objects and arrays comparison.
|
|
|
|
|
aStack = aStack || [];
|
|
|
|
|
bStack = bStack || [];
|
|
|
|
|
var length = aStack.length;
|
|
|
|
|
while (length--) {
|
|
|
|
|
// Linear search. Performance is inversely proportional to the number of
|
|
|
|
|
// unique nested structures.
|
|
|
|
|
if (aStack[length] === a) return bStack[length] === b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add the first object to the stack of traversed objects.
|
|
|
|
|
aStack.push(a);
|
|
|
|
|
bStack.push(b);
|
|
|
|
|
|
|
|
|
|
// Recursively compare objects and arrays.
|
|
|
|
|
if (areArrays) {
|
|
|
|
|
// Compare array lengths to determine if a deep comparison is necessary.
|
|
|
|
|
length = a.length;
|
|
|
|
|
if (length !== b.length) return false;
|
|
|
|
|
// Deep compare the contents, ignoring non-numeric properties.
|
|
|
|
|
while (length--) {
|
|
|
|
|
if (!eq(a[length], b[length], aStack, bStack)) return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Deep compare objects.
|
|
|
|
|
var keys = _.keys(a), key;
|
|
|
|
|
length = keys.length;
|
|
|
|
|
// Ensure that both objects contain the same number of properties before comparing deep equality.
|
|
|
|
|
if (_.keys(b).length !== length) return false;
|
|
|
|
|
while (length--) {
|
|
|
|
|
// Deep compare each member
|
|
|
|
|
key = keys[length];
|
|
|
|
|
if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Remove the first object from the stack of traversed objects.
|
|
|
|
|
aStack.pop();
|
|
|
|
|
bStack.pop();
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Perform a deep comparison to check if two objects are equal.
|
|
|
|
|
_.isEqual = function(a, b) {
|
|
|
|
|
return eq(a, b);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Is a given array, string, or object empty?
|
|
|
|
|
// An "empty" object has no enumerable own-properties.
|
|
|
|
|
_.isEmpty = function(obj) {
|
|
|
|
|
if (obj == null) return true;
|
|
|
|
|
if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
|
|
|
|
|
return _.keys(obj).length === 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Is a given value a DOM element?
|
|
|
|
|
_.isElement = function(obj) {
|
|
|
|
|
return !!(obj && obj.nodeType === 1);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Is a given value an array?
|
|
|
|
|
// Delegates to ECMA5's native Array.isArray
|
|
|
|
|
_.isArray = nativeIsArray || function(obj) {
|
|
|
|
|
return toString.call(obj) === '[object Array]';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Is a given variable an object?
|
|
|
|
|
_.isObject = function(obj) {
|
|
|
|
|
var type = typeof obj;
|
|
|
|
|
return type === 'function' || type === 'object' && !!obj;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError.
|
|
|
|
|
_.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) {
|
|
|
|
|
_['is' + name] = function(obj) {
|
|
|
|
|
return toString.call(obj) === '[object ' + name + ']';
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Define a fallback version of the method in browsers (ahem, IE < 9), where
|
|
|
|
|
// there isn't any inspectable "Arguments" type.
|
|
|
|
|
if (!_.isArguments(arguments)) {
|
|
|
|
|
_.isArguments = function(obj) {
|
|
|
|
|
return _.has(obj, 'callee');
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
|
|
|
|
|
// IE 11 (#1621), and in Safari 8 (#1929).
|
|
|
|
|
if ( true && typeof Int8Array != 'object') {
|
|
|
|
|
_.isFunction = function(obj) {
|
|
|
|
|
return typeof obj == 'function' || false;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Is a given object a finite number?
|
|
|
|
|
_.isFinite = function(obj) {
|
|
|
|
|
return isFinite(obj) && !isNaN(parseFloat(obj));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
|
|
|
|
|
_.isNaN = function(obj) {
|
|
|
|
|
return _.isNumber(obj) && obj !== +obj;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Is a given value a boolean?
|
|
|
|
|
_.isBoolean = function(obj) {
|
|
|
|
|
return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Is a given value equal to null?
|
|
|
|
|
_.isNull = function(obj) {
|
|
|
|
|
return obj === null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Is a given variable undefined?
|
|
|
|
|
_.isUndefined = function(obj) {
|
|
|
|
|
return obj === void 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Shortcut function for checking if an object has a given property directly
|
|
|
|
|
// on itself (in other words, not on a prototype).
|
|
|
|
|
_.has = function(obj, key) {
|
|
|
|
|
return obj != null && hasOwnProperty.call(obj, key);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Utility Functions
|
|
|
|
|
// -----------------
|
|
|
|
|
|
|
|
|
|
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
|
|
|
|
|
// previous owner. Returns a reference to the Underscore object.
|
|
|
|
|
_.noConflict = function() {
|
|
|
|
|
root._ = previousUnderscore;
|
|
|
|
|
return this;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Keep the identity function around for default iteratees.
|
|
|
|
|
_.identity = function(value) {
|
|
|
|
|
return value;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Predicate-generating functions. Often useful outside of Underscore.
|
|
|
|
|
_.constant = function(value) {
|
|
|
|
|
return function() {
|
|
|
|
|
return value;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_.noop = function(){};
|
|
|
|
|
|
|
|
|
|
_.property = property;
|
|
|
|
|
|
|
|
|
|
// Generates a function for a given object that returns a given property.
|
|
|
|
|
_.propertyOf = function(obj) {
|
|
|
|
|
return obj == null ? function(){} : function(key) {
|
|
|
|
|
return obj[key];
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Returns a predicate for checking whether an object has a given set of
|
|
|
|
|
// `key:value` pairs.
|
|
|
|
|
_.matcher = _.matches = function(attrs) {
|
|
|
|
|
attrs = _.extendOwn({}, attrs);
|
|
|
|
|
return function(obj) {
|
|
|
|
|
return _.isMatch(obj, attrs);
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Run a function **n** times.
|
|
|
|
|
_.times = function(n, iteratee, context) {
|
|
|
|
|
var accum = Array(Math.max(0, n));
|
|
|
|
|
iteratee = optimizeCb(iteratee, context, 1);
|
|
|
|
|
for (var i = 0; i < n; i++) accum[i] = iteratee(i);
|
|
|
|
|
return accum;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Return a random integer between min and max (inclusive).
|
|
|
|
|
_.random = function(min, max) {
|
|
|
|
|
if (max == null) {
|
|
|
|
|
max = min;
|
|
|
|
|
min = 0;
|
|
|
|
|
}
|
|
|
|
|
return min + Math.floor(Math.random() * (max - min + 1));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// A (possibly faster) way to get the current timestamp as an integer.
|
|
|
|
|
_.now = Date.now || function() {
|
|
|
|
|
return new Date().getTime();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// List of HTML entities for escaping.
|
|
|
|
|
var escapeMap = {
|
|
|
|
|
'&': '&',
|
|
|
|
|
'<': '<',
|
|
|
|
|
'>': '>',
|
|
|
|
|
'"': '"',
|
|
|
|
|
"'": ''',
|
|
|
|
|
'`': '`'
|
|
|
|
|
};
|
|
|
|
|
var unescapeMap = _.invert(escapeMap);
|
|
|
|
|
|
|
|
|
|
// Functions for escaping and unescaping strings to/from HTML interpolation.
|
|
|
|
|
var createEscaper = function(map) {
|
|
|
|
|
var escaper = function(match) {
|
|
|
|
|
return map[match];
|
|
|
|
|
};
|
|
|
|
|
// Regexes for identifying a key that needs to be escaped
|
|
|
|
|
var source = '(?:' + _.keys(map).join('|') + ')';
|
|
|
|
|
var testRegexp = RegExp(source);
|
|
|
|
|
var replaceRegexp = RegExp(source, 'g');
|
|
|
|
|
return function(string) {
|
|
|
|
|
string = string == null ? '' : '' + string;
|
|
|
|
|
return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
_.escape = createEscaper(escapeMap);
|
|
|
|
|
_.unescape = createEscaper(unescapeMap);
|
|
|
|
|
|
|
|
|
|
// If the value of the named `property` is a function then invoke it with the
|
|
|
|
|
// `object` as context; otherwise, return it.
|
|
|
|
|
_.result = function(object, property, fallback) {
|
|
|
|
|
var value = object == null ? void 0 : object[property];
|
|
|
|
|
if (value === void 0) {
|
|
|
|
|
value = fallback;
|
|
|
|
|
}
|
|
|
|
|
return _.isFunction(value) ? value.call(object) : value;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Generate a unique integer id (unique within the entire client session).
|
|
|
|
|
// Useful for temporary DOM ids.
|
|
|
|
|
var idCounter = 0;
|
|
|
|
|
_.uniqueId = function(prefix) {
|
|
|
|
|
var id = ++idCounter + '';
|
|
|
|
|
return prefix ? prefix + id : id;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// By default, Underscore uses ERB-style template delimiters, change the
|
|
|
|
|
// following template settings to use alternative delimiters.
|
|
|
|
|
_.templateSettings = {
|
|
|
|
|
evaluate : /<%([\s\S]+?)%>/g,
|
|
|
|
|
interpolate : /<%=([\s\S]+?)%>/g,
|
|
|
|
|
escape : /<%-([\s\S]+?)%>/g
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// When customizing `templateSettings`, if you don't want to define an
|
|
|
|
|
// interpolation, evaluation or escaping regex, we need one that is
|
|
|
|
|
// guaranteed not to match.
|
|
|
|
|
var noMatch = /(.)^/;
|
|
|
|
|
|
|
|
|
|
// Certain characters need to be escaped so that they can be put into a
|
|
|
|
|
// string literal.
|
|
|
|
|
var escapes = {
|
|
|
|
|
"'": "'",
|
|
|
|
|
'\\': '\\',
|
|
|
|
|
'\r': 'r',
|
|
|
|
|
'\n': 'n',
|
|
|
|
|
'\u2028': 'u2028',
|
|
|
|
|
'\u2029': 'u2029'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
|
|
|
|
|
|
|
|
|
|
var escapeChar = function(match) {
|
|
|
|
|
return '\\' + escapes[match];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// JavaScript micro-templating, similar to John Resig's implementation.
|
|
|
|
|
// Underscore templating handles arbitrary delimiters, preserves whitespace,
|
|
|
|
|
// and correctly escapes quotes within interpolated code.
|
|
|
|
|
// NB: `oldSettings` only exists for backwards compatibility.
|
|
|
|
|
_.template = function(text, settings, oldSettings) {
|
|
|
|
|
if (!settings && oldSettings) settings = oldSettings;
|
|
|
|
|
settings = _.defaults({}, settings, _.templateSettings);
|
|
|
|
|
|
|
|
|
|
// Combine delimiters into one regular expression via alternation.
|
|
|
|
|
var matcher = RegExp([
|
|
|
|
|
(settings.escape || noMatch).source,
|
|
|
|
|
(settings.interpolate || noMatch).source,
|
|
|
|
|
(settings.evaluate || noMatch).source
|
|
|
|
|
].join('|') + '|$', 'g');
|
|
|
|
|
|
|
|
|
|
// Compile the template source, escaping string literals appropriately.
|
|
|
|
|
var index = 0;
|
|
|
|
|
var source = "__p+='";
|
|
|
|
|
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
|
|
|
|
|
source += text.slice(index, offset).replace(escaper, escapeChar);
|
|
|
|
|
index = offset + match.length;
|
|
|
|
|
|
|
|
|
|
if (escape) {
|
|
|
|
|
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
|
|
|
|
|
} else if (interpolate) {
|
|
|
|
|
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
|
|
|
|
|
} else if (evaluate) {
|
|
|
|
|
source += "';\n" + evaluate + "\n__p+='";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Adobe VMs need the match returned to produce the correct offest.
|
|
|
|
|
return match;
|
|
|
|
|
});
|
|
|
|
|
source += "';\n";
|
|
|
|
|
|
|
|
|
|
// If a variable is not specified, place data values in local scope.
|
|
|
|
|
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
|
|
|
|
|
|
|
|
|
|
source = "var __t,__p='',__j=Array.prototype.join," +
|
|
|
|
|
"print=function(){__p+=__j.call(arguments,'');};\n" +
|
|
|
|
|
source + 'return __p;\n';
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
var render = new Function(settings.variable || 'obj', '_', source);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
e.source = source;
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var template = function(data) {
|
|
|
|
|
return render.call(this, data, _);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Provide the compiled source as a convenience for precompilation.
|
|
|
|
|
var argument = settings.variable || 'obj';
|
|
|
|
|
template.source = 'function(' + argument + '){\n' + source + '}';
|
|
|
|
|
|
|
|
|
|
return template;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Add a "chain" function. Start chaining a wrapped Underscore object.
|
|
|
|
|
_.chain = function(obj) {
|
|
|
|
|
var instance = _(obj);
|
|
|
|
|
instance._chain = true;
|
|
|
|
|
return instance;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// OOP
|
|
|
|
|
// ---------------
|
|
|
|
|
// If Underscore is called as a function, it returns a wrapped object that
|
|
|
|
|
// can be used OO-style. This wrapper holds altered versions of all the
|
|
|
|
|
// underscore functions. Wrapped objects may be chained.
|
|
|
|
|
|
|
|
|
|
// Helper function to continue chaining intermediate results.
|
|
|
|
|
var result = function(instance, obj) {
|
|
|
|
|
return instance._chain ? _(obj).chain() : obj;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Add your own custom functions to the Underscore object.
|
|
|
|
|
_.mixin = function(obj) {
|
|
|
|
|
_.each(_.functions(obj), function(name) {
|
|
|
|
|
var func = _[name] = obj[name];
|
|
|
|
|
_.prototype[name] = function() {
|
|
|
|
|
var args = [this._wrapped];
|
|
|
|
|
push.apply(args, arguments);
|
|
|
|
|
return result(this, func.apply(_, args));
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Add all of the Underscore functions to the wrapper object.
|
|
|
|
|
_.mixin(_);
|
|
|
|
|
|
|
|
|
|
// Add all mutator Array functions to the wrapper.
|
|
|
|
|
_.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
|
|
|
|
var method = ArrayProto[name];
|
|
|
|
|
_.prototype[name] = function() {
|
|
|
|
|
var obj = this._wrapped;
|
|
|
|
|
method.apply(obj, arguments);
|
|
|
|
|
if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
|
|
|
|
|
return result(this, obj);
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Add all accessor Array functions to the wrapper.
|
|
|
|
|
_.each(['concat', 'join', 'slice'], function(name) {
|
|
|
|
|
var method = ArrayProto[name];
|
|
|
|
|
_.prototype[name] = function() {
|
|
|
|
|
return result(this, method.apply(this._wrapped, arguments));
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Extracts the result from a wrapped and chained object.
|
|
|
|
|
_.prototype.value = function() {
|
|
|
|
|
return this._wrapped;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Provide unwrapping proxy for some methods used in engine operations
|
|
|
|
|
// such as arithmetic and JSON stringification.
|
|
|
|
|
_.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
|
|
|
|
|
|
|
|
|
|
_.prototype.toString = function() {
|
|
|
|
|
return '' + this._wrapped;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// AMD registration happens at the end for compatibility with AMD loaders
|
|
|
|
|
// that may not enforce next-turn semantics on modules. Even though general
|
|
|
|
|
// practice for AMD registration is to be anonymous, underscore registers
|
|
|
|
|
// as a named module because, like jQuery, it is a base library that is
|
|
|
|
|
// popular enough to be bundled in a third party lib, but not be part of
|
|
|
|
|
// an AMD load request. Those cases could generate an error when an
|
|
|
|
|
// anonymous define() is called outside of a loader request.
|
|
|
|
|
if (typeof define === 'function' && define.amd) {
|
|
|
|
|
define('underscore', [], function() {
|
|
|
|
|
return _;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}.call(this));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 941:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
var basiccreds_1 = __webpack_require__(12);
|
|
|
|
|
exports.BasicCredentialHandler = basiccreds_1.BasicCredentialHandler;
|
|
|
|
|
var bearertoken_1 = __webpack_require__(571);
|
|
|
|
|
exports.BearerCredentialHandler = bearertoken_1.BearerCredentialHandler;
|
|
|
|
|
var ntlm_1 = __webpack_require__(525);
|
|
|
|
|
exports.NtlmCredentialHandler = ntlm_1.NtlmCredentialHandler;
|
|
|
|
|
var personalaccesstoken_1 = __webpack_require__(327);
|
|
|
|
|
exports.PersonalAccessTokenCredentialHandler = personalaccesstoken_1.PersonalAccessTokenCredentialHandler;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 943:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
|
|
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
|
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
|
|
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
|
|
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
|
|
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
|
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
|
|
if (mod && mod.__esModule) return mod;
|
|
|
|
|
var result = {};
|
|
|
|
|
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
|
|
|
result["default"] = mod;
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
const exec_1 = __webpack_require__(986);
|
|
|
|
|
const io = __importStar(__webpack_require__(1));
|
|
|
|
|
const fs_1 = __webpack_require__(747);
|
|
|
|
|
function getTarPath() {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
// Explicitly use BSD Tar on Windows
|
|
|
|
|
const IS_WINDOWS = process.platform === "win32";
|
|
|
|
|
if (IS_WINDOWS) {
|
|
|
|
|
const systemTar = `${process.env["windir"]}\\System32\\tar.exe`;
|
|
|
|
|
if (fs_1.existsSync(systemTar)) {
|
|
|
|
|
return systemTar;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return yield io.which("tar", true);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
function execTar(args) {
|
|
|
|
|
var _a, _b;
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
try {
|
|
|
|
|
const tarPath = yield getTarPath();
|
|
|
|
|
const tarExec = process.platform !== "win32" ? `sudo ${tarPath}` : tarPath;
|
|
|
|
|
yield exec_1.exec(`"${tarExec}"`, args);
|
|
|
|
|
}
|
|
|
|
|
catch (error) {
|
|
|
|
|
const IS_WINDOWS = process.platform === "win32";
|
|
|
|
|
if (IS_WINDOWS) {
|
|
|
|
|
throw new Error(`Tar failed with error: ${(_a = error) === null || _a === void 0 ? void 0 : _a.message}. Ensure BSD tar is installed and on the PATH.`);
|
|
|
|
|
}
|
|
|
|
|
throw new Error(`Tar failed with error: ${(_b = error) === null || _b === void 0 ? void 0 : _b.message}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
function extractTar(archivePath, targetDirectory) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
// Create directory to extract tar into
|
|
|
|
|
yield io.mkdirP(targetDirectory);
|
|
|
|
|
const args = ["-xz", "-f", archivePath, "-C", targetDirectory];
|
|
|
|
|
yield execTar(args);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.extractTar = extractTar;
|
|
|
|
|
function createTar(archivePath, sourceDirectory) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
const args = ["-cz", "-f", archivePath, "-C", sourceDirectory, "."];
|
|
|
|
|
yield execTar(args);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.createTar = createTar;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
|
|
|
|
/***/ 986:
|
|
|
|
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
|
|
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
|
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
|
|
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
|
|
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
|
|
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
|
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
const tr = __webpack_require__(9);
|
|
|
|
|
/**
|
|
|
|
|
* Exec a command.
|
|
|
|
|
* Output will be streamed to the live console.
|
|
|
|
|
* Returns promise with return code
|
|
|
|
|
*
|
|
|
|
|
* @param commandLine command to execute (can include additional args). Must be correctly escaped.
|
|
|
|
|
* @param args optional arguments for tool. Escaping is handled by the lib.
|
|
|
|
|
* @param options optional exec options. See ExecOptions
|
|
|
|
|
* @returns Promise<number> exit code
|
|
|
|
|
*/
|
|
|
|
|
function exec(commandLine, args, options) {
|
|
|
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
|
|
|
const commandArgs = tr.argStringToArray(commandLine);
|
|
|
|
|
if (commandArgs.length === 0) {
|
|
|
|
|
throw new Error(`Parameter 'commandLine' cannot be null or empty.`);
|
|
|
|
|
}
|
|
|
|
|
// Path to tool to execute should be first arg
|
|
|
|
|
const toolPath = commandArgs[0];
|
|
|
|
|
args = commandArgs.slice(1).concat(args || []);
|
|
|
|
|
const runner = new tr.ToolRunner(toolPath, args, options);
|
|
|
|
|
return runner.exec();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
exports.exec = exec;
|
|
|
|
|
//# sourceMappingURL=exec.js.map
|
|
|
|
|
|
|
|
|
|
/***/ })
|
|
|
|
|
|
|
|
|
|
/******/ });
|