diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index e229216..2fb9d73 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -12,8 +12,11 @@ A clear and concise description of what the bug is. **My Action Config** ```yaml -on: - push: +on: push +name: Publish Website +jobs: + web-deploy: + name: 🚀 Deploy website every commit # !!!!!!! TODO Fill Out !!!!!!! ``` diff --git a/.github/workflows/ftp.yml b/.github/workflows/ftp.yml index 4d9c685..aed29bc 100644 --- a/.github/workflows/ftp.yml +++ b/.github/workflows/ftp.yml @@ -7,7 +7,7 @@ on: branches: [ master ] jobs: - deploy: # make sure the action works on a clean machine without building + deploy: name: 🚀 Deploy website every commit runs-on: ubuntu-latest steps: diff --git a/.github/workflows/ftps.yml b/.github/workflows/ftps.yml index 6bfa9f5..eba473f 100644 --- a/.github/workflows/ftps.yml +++ b/.github/workflows/ftps.yml @@ -7,7 +7,7 @@ on: branches: [ master ] jobs: - deploy: # make sure the action works on a clean machine without building + deploy: name: 🚀 Deploy website every commit runs-on: ubuntu-latest steps: diff --git a/README.md b/README.md index dff12ee..4150e56 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,19 @@ Automate deploying websites and more with this GitHub action ![FTP test](https://github.com/SamKirkland/FTP-Deploy-Action/workflows/FTP%20Test/badge.svg) ![FTPS test](https://github.com/SamKirkland/FTP-Deploy-Action/workflows/FTPS%20Test/badge.svg) +![npm](https://img.shields.io/npm/v/@samkirkland/ftp-deploy?style=flat-square) +![npm](https://img.shields.io/npm/dt/@samkirkland/ftp-deploy) + --- ### Usage Example Place the following in `/.github/workflows/main.yml` ```yml on: push -name: Publish Website +name: 🚀 Deploy website on push jobs: web-deploy: - name: 🚀 Deploy website every commit + name: 🎉 Deploy runs-on: ubuntu-latest steps: - name: 🚚 Get latest code @@ -66,10 +69,10 @@ I strongly recommend you store your `password` as a secret. | `local-dir` | No | `./myFolderToPublish/` | `./` | Path to upload to on the server, must end with trailing slash `/` | | `server-dir` | No | `public_html/www/` | `./` | Folder to upload from, must end with trailing slash `/` | | `state-name` | No | `folder/.sync-state.json` | `.ftp-deploy-sync-state.json` | Custom | -| `dry-run` | No | `true` | `false` | :warning: todo - Prints which modifications will be made with current config options, but doesn't actually make any changes | -| `dangerous-clean-slate` | No | `true` | `false` | :warning: todo - Deletes ALL contents of server-dir, even items in excluded with 'exclude' argument | -| `include` | No | | `` | :warning: todo - An array of glob patterns, these files will always be included in the publish/delete process - even if no change occurred | -| `exclude` | No | | `.git*` `.git*/**` `node_modules/**` `node_modules/**/*` | :warning: todo - An array of glob patterns, these files will not be included in the publish/delete process | +| `dry-run` | No | `true` | `false` | Prints which modifications will be made with current config options, but doesn't actually make any changes | +| `dangerous-clean-slate` | No | `true` | `false` | Deletes ALL contents of server-dir, even items in excluded with 'exclude' argument | +| `include` | No | | `` | :warning: not implemented yet - An array of glob patterns, these files will always be included in the publish/delete process - even if no change occurred | +| `exclude` | No | | `.git*` `.git*/**` `node_modules/**` `node_modules/**/*` | An array of glob patterns, these files will not be included in the publish/delete process | | `log-level` | No | `info` | `info` | `warn`: only important/warning info, `info`: default, log important/warning info & progress info, `debug`: log everything for debugging | @@ -79,10 +82,10 @@ Make sure you have an npm script named 'build'. This config should work for most ```yml on: push -name: Publish Website +name: 🚀 Deploy website on push jobs: web-deploy: - name: 🚀 Deploy website every commit + name: 🎉 Deploy runs-on: ubuntu-latest steps: - name: 🚚 Get latest code @@ -109,10 +112,10 @@ jobs: #### FTPS ```yml on: push -name: Publish Website Dry Run +name: 🚀 Deploy website on push jobs: web-deploy: - name: 🚀 Deploy website every commit + name: 🎉 Deploy runs-on: ubuntu-latest steps: - name: 🚚 Get latest code @@ -125,16 +128,17 @@ jobs: username: myFtpUserName password: ${{ secrets.password }} protocol: ftps + port: 1234 # todo replace with your web hosts ftps port ``` #### Log only dry run: Use this mode for testing Ouputs a list of files that will be created/modified to sync your source without making any actual changes ```yml on: push -name: Publish Website Dry Run +name: 🚀 Deploy website on push jobs: web-deploy: - name: 🚀 Deploy website every commit + name: 🎉 Deploy runs-on: ubuntu-latest steps: - name: 🚚 Get latest code diff --git a/dist/index.js b/dist/index.js index 42f3013..5172b10 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,70 +1,394 @@ 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 -/******/ var threw = true; -/******/ try { -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ threw = false; -/******/ } finally { -/******/ if(threw) delete installedModules[moduleId]; -/******/ } -/******/ -/******/ // 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__(131); -/******/ }; -/******/ -/******/ // run startup -/******/ return startup(); -/******/ }) -/************************************************************************/ -/******/ ({ +/******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({ -/***/ 16: -/***/ (function(module) { +/***/ 351: +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { -module.exports = require("tls"); +"use strict"; + +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 os = __importStar(__webpack_require__(87)); +/** + * Commands + * + * Command Format: + * ::name key=value,key=value::message + * + * Examples: + * ::warning::This is the message + * ::set-env name=MY_VAR::some value + */ +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 += ' '; + let first = true; + for (const key in this.properties) { + if (this.properties.hasOwnProperty(key)) { + const val = this.properties[key]; + if (val) { + if (first) { + first = false; + } + else { + cmdStr += ','; + } + cmdStr += `${key}=${escapeProperty(val)}`; + } + } + } + } + cmdStr += `${CMD_STRING}${escapeData(this.message)}`; + return cmdStr; + } +} +/** + * Sanitizes an input into a string so it can be passed into issueCommand safely + * @param input input to sanitize into a string + */ +function toCommandValue(input) { + if (input === null || input === undefined) { + return ''; + } + else if (typeof input === 'string' || input instanceof String) { + return input; + } + return JSON.stringify(input); +} +exports.toCommandValue = toCommandValue; +function escapeData(s) { + return toCommandValue(s) + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A'); +} +function escapeProperty(s) { + return toCommandValue(s) + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A') + .replace(/:/g, '%3A') + .replace(/,/g, '%2C'); +} +//# sourceMappingURL=command.js.map /***/ }), -/***/ 87: -/***/ (function(module) { +/***/ 186: +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { -module.exports = require("os"); +"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 command_1 = __webpack_require__(351); +const os = __importStar(__webpack_require__(87)); +const path = __importStar(__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. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function exportVariable(name, val) { + const convertedVal = command_1.toCommandValue(val); + process.env[name] = convertedVal; + command_1.issueCommand('set-env', { name }, convertedVal); +} +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. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function setOutput(name, value) { + command_1.issueCommand('set-output', { name }, value); +} +exports.setOutput = setOutput; +/** + * Enables or disables the echoing of commands into stdout for the rest of the step. + * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set. + * + */ +function setCommandEcho(enabled) { + command_1.issue('echo', enabled ? 'on' : 'off'); +} +exports.setCommandEcho = setCommandEcho; +//----------------------------------------------------------------------- +// 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 +//----------------------------------------------------------------------- +/** + * Gets whether Actions Step Debug is on or not + */ +function isDebug() { + return process.env['RUNNER_DEBUG'] === '1'; +} +exports.isDebug = isDebug; +/** + * 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. Errors will be converted to string via toString() + */ +function error(message) { + command_1.issue('error', message instanceof Error ? message.toString() : message); +} +exports.error = error; +/** + * Adds an warning issue + * @param message warning issue message. Errors will be converted to string via toString() + */ +function warning(message) { + command_1.issue('warning', message instanceof Error ? message.toString() : 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. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +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 /***/ }), -/***/ 131: -/***/ (function(__unusedmodule, exports, __webpack_require__) { +/***/ 589: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +module.exports = +/******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ 5176: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_149__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.createFilter = void 0; +const normalize_1 = __nested_webpack_require_149__(7561); +const util_1 = __nested_webpack_require_149__(9735); +function createFilter(options, ...args) { + let criteria = args.length <= 1 ? args[0] : args; + let filters = normalize_1.normalize(criteria, options); + pathFilter[util_1._filters] = filters; + return pathFilter; + function pathFilter(...args) { + // Does the file path match any of the exclude filters? + let exclude = filters.exclude.some((filter) => filter(...args)); + if (exclude) { + return false; + } + if (filters.include.length === 0) { + // Include everything that's not excluded + return true; + } + // Does the file path match any of the include filters? + let include = filters.include.some((filter) => filter(...args)); + return include; + } +} +exports.createFilter = createFilter; +//# sourceMappingURL=create-filter.js.map + +/***/ }), + +/***/ 2405: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_1276__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.filePathFilter = void 0; +const create_filter_1 = __nested_webpack_require_1276__(5176); +function filePathFilter(...args) { + return create_filter_1.createFilter({}, ...args); +} +exports.filePathFilter = filePathFilter; +//# sourceMappingURL=file-path-filter.js.map + +/***/ }), + +/***/ 3410: +/***/ (function(module, exports, __nested_webpack_require_1701__) { "use strict"; @@ -75,478 +399,1099 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi if (k2 === undefined) k2 = k; o[k2] = m[k]; })); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); }; -Object.defineProperty(exports, "__esModule", { value: true }); -const core = __importStar(__webpack_require__(470)); -const ftp_deploy_1 = __webpack_require__(372); -async function runDeployment() { - const args = { - server: core.getInput("server", { required: true }), - username: core.getInput("username", { required: true }), - password: core.getInput("password", { required: true }), - protocol: core.getInput("protocol"), - port: core.getInput("port"), - "local-dir": core.getInput("local-dir"), - "server-dir": core.getInput("server-dir"), - "state-name": core.getInput("state-name"), - "dry-run": core.getInput("dry-run"), - "dangerous-clean-slate": core.getInput("dangerous-clean-slate"), - "include": core.getInput("include"), - "exclude": core.getInput("exclude"), - "log-level": core.getInput("log-level") // todo fix - }; - try { - await ftp_deploy_1.deploy(args); - } - catch (error) { - core.setFailed(error); - } +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.filePathFilter = void 0; +const file_path_filter_1 = __nested_webpack_require_1701__(2405); +Object.defineProperty(exports, "filePathFilter", ({ enumerable: true, get: function () { return file_path_filter_1.filePathFilter; } })); +__exportStar(__nested_webpack_require_1701__(3225), exports); +var create_filter_1 = __nested_webpack_require_1701__(5176); +Object.defineProperty(exports, "createFilter", ({ enumerable: true, get: function () { return create_filter_1.createFilter; } })); +// Export `filePathFilter` as a named export and the default export +exports.default = file_path_filter_1.filePathFilter; +// CommonJS default export hack +/* eslint-env commonjs */ +if ( true && typeof module.exports === "object") { + module.exports = Object.assign(module.exports.default, module.exports); } -runDeployment(); - +//# sourceMappingURL=index.js.map /***/ }), -/***/ 372: -/***/ (function(module, __unusedexports, __webpack_require__) { - -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 -/******/ var threw = true; -/******/ try { -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ threw = false; -/******/ } finally { -/******/ if(threw) delete installedModules[moduleId]; -/******/ } -/******/ -/******/ // 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__(611); -/******/ }; -/******/ -/******/ // run startup -/******/ return startup(); -/******/ }) -/************************************************************************/ -/******/ ({ - -/***/ 16: -/***/ (function(module) { - -module.exports = __webpack_require__(16); - -/***/ }), - -/***/ 52: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_1666__) { +/***/ 7561: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_3157__) => { "use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FTPContext = exports.FTPError = void 0; -const net_1 = __nested_webpack_require_1666__(631); -const parseControlResponse_1 = __nested_webpack_require_1666__(948); +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.normalize = void 0; +const globToRegExp = __nested_webpack_require_3157__(7117); +const path = __nested_webpack_require_3157__(5622); +const util_1 = __nested_webpack_require_3157__(9735); /** - * Describes an FTP server error response including the FTP response code. + * Normalizes the user-provided filter criteria. The normalized form is a `Filters` object + * whose `include` and `exclude` properties are both `FilterFunction` arrays. */ -class FTPError extends Error { - constructor(res) { - super(res.message); - this.name = this.constructor.name; - this.code = res.code; +function normalize(criteria, opts) { + let filters = { + include: [], + exclude: [], + }; + let options = normalizeOptions(opts); + // Convert each criterion to a FilterFunction + let tuples = normalizeCriteria(criteria, options); + // Populate the `include` and `exclude` arrays + for (let [filter, filterFunction] of tuples) { + filters[filter].push(filterFunction); + } + return filters; +} +exports.normalize = normalize; +/** + * Fills-in defaults for any options that weren't specified by the caller. + */ +function normalizeOptions(options) { + return { + // TODO: Remove the "getPath" fallback in the next minor release + map: options.map || options.getPath || String, + sep: options.sep || path.sep, + }; +} +/** + * Creates a `FilterFunction` for each given criterion. + */ +function normalizeCriteria(criteria, options, filter) { + let tuples = []; + if (Array.isArray(criteria)) { + for (let criterion of criteria) { + tuples.push(...normalizeCriteria(criterion, options, filter)); + } + } + else if (util_1.isPathFilter(criteria)) { + for (let filterFunction of criteria[util_1._filters].include) { + tuples.push(["include", filterFunction]); + } + for (let filterFunction of criteria[util_1._filters].exclude) { + tuples.push(["exclude", filterFunction]); + } + } + else if (util_1.isFilterCriterion(criteria)) { + tuples.push(normalizeCriterion(criteria, options, filter)); + } + else if (criteria && typeof criteria === "object" && !filter) { + if (criteria.include !== undefined) { + tuples.push(...normalizeCriteria(criteria.include, options, "include")); + } + if (criteria.exclude !== undefined) { + tuples.push(...normalizeCriteria(criteria.exclude, options, "exclude")); + } + } + else { + throw new Error(`Invalid filter criteria: ${criteria}`); + } + return tuples; +} +/** + * Creates a `FilterFunction` for the given criterion. + * + * @param criteria - One or more filter critiera + * @param options - Options for how the `FilterFunction` should behave + * @param filter - The type of filter. Defaults to `include`, except for glob patterns that start with "!" + */ +function normalizeCriterion(criterion, options, filter) { + const globOptions = { extended: true, globstar: true }; + let type = typeof criterion; + let filterFunction; + if (type === "function") { + filterFunction = criterion; + } + else if (type === "boolean") { + let bool = criterion; + filterFunction = function booleanFilter() { + return bool; + }; + } + else if (type === "string") { + let glob = criterion; + let invert = false; + if (glob.startsWith("!")) { + glob = glob.substr(1); + invert = Boolean(filter); + filter = filter || "exclude"; + } + let pattern = globToRegExp(glob, globOptions); + filterFunction = createGlobFilter(pattern, options, invert); + } + else if (criterion instanceof RegExp) { + let pattern = criterion; + let { map } = options; + filterFunction = function regExpFilter(...args) { + let filePath = map(...args); + return pattern.test(filePath); + }; + } + else { + throw new Error(`Invalid filter criteria: ${criterion}`); + } + return [filter || "include", filterFunction]; +} +/** + * Creates a `FilterFunction` for filtering based on glob patterns + */ +function createGlobFilter(pattern, options, invert) { + let { map, sep } = options; + return function globFilter(...args) { + let filePath = map(...args); + if (sep !== "/") { + // Glob patterns always expect forward slashes, even on Windows + filePath = filePath.replace(new RegExp("\\" + sep, "g"), "/"); + } + let match = pattern.test(filePath); + return invert ? !match : match; + }; +} +//# sourceMappingURL=normalize.js.map + +/***/ }), + +/***/ 3225: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +//# sourceMappingURL=types.js.map + +/***/ }), + +/***/ 9735: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.isPathFilter = exports.isFilterCriterion = exports._filters = void 0; +/** + * Symbol used to store the underlying filters of a `pathFilter()` function. + */ +exports._filters = Symbol("_filters"); +/** + * Determines whether the given value is a `FilterCriterion`. + */ +function isFilterCriterion(value) { + let type = typeof value; + return type === "string" || + type === "boolean" || + type === "function" || + value instanceof RegExp; +} +exports.isFilterCriterion = isFilterCriterion; +/** + * Determines whether the given value is one of our internal `pathFilter()` functions. + */ +function isPathFilter(value) { + let fn = value; + return fn && + typeof fn === "function" && + typeof fn[exports._filters] === "object"; +} +exports.isPathFilter = isPathFilter; +//# sourceMappingURL=util.js.map + +/***/ }), + +/***/ 504: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.asyncForEach = void 0; +/** + * Simultaneously processes all items in the given array. + * + * @param array - The array to iterate over + * @param iterator - The function to call for each item in the array + * @param done - The function to call when all iterators have completed + * + * @internal + */ +function asyncForEach(array, iterator, done) { + if (!Array.isArray(array)) { + throw new TypeError(`${array} is not an array`); + } + if (array.length === 0) { + // NOTE: Normally a bad idea to mix sync and async, but it's safe here because + // of the way that this method is currently used by DirectoryReader. + done(); + return; + } + // Simultaneously process all items in the array. + let pending = array.length; + for (let item of array) { + iterator(item, callback); + } + function callback() { + if (--pending === 0) { + done(); + } } } -exports.FTPError = FTPError; +exports.asyncForEach = asyncForEach; +//# sourceMappingURL=for-each.js.map + +/***/ }), + +/***/ 5833: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_10017__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.readdirAsync = void 0; +const fs = __nested_webpack_require_10017__(5747); +const directory_reader_1 = __nested_webpack_require_10017__(4918); +const for_each_1 = __nested_webpack_require_10017__(504); +const asyncFacade = { fs, forEach: for_each_1.asyncForEach }; +function readdirAsync(dir, options, callback) { + if (typeof options === "function") { + callback = options; + options = undefined; + } + let promise = new Promise((resolve, reject) => { + let results = []; + let reader = new directory_reader_1.DirectoryReader(dir, options, asyncFacade); + let stream = reader.stream; + stream.on("error", (err) => { + reject(err); + stream.pause(); + }); + stream.on("data", (result) => { + results.push(result); + }); + stream.on("end", () => { + resolve(results); + }); + }); + if (callback) { + promise.then((results) => callback(null, results), (err) => callback(err, undefined)); + } + else { + return promise; + } +} +exports.readdirAsync = readdirAsync; +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ 8188: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.callOnce = exports.safeCall = void 0; /** - * FTPContext holds the control and data sockets of an FTP connection and provides a - * simplified way to interact with an FTP server, handle responses, errors and timeouts. + * Calls a function with the given arguments, and ensures that the error-first callback is _always_ + * invoked exactly once, even if the function throws an error. * - * It doesn't implement or use any FTP commands. It's only a foundation to make writing an FTP - * client as easy as possible. You won't usually instantiate this, but use `Client`. + * @param fn - The function to invoke + * @param args - The arguments to pass to the function. The final argument must be a callback function. + * + * @internal */ -class FTPContext { - /** - * Instantiate an FTP context. - * - * @param timeout - Timeout in milliseconds to apply to control and data connections. Use 0 for no timeout. - * @param encoding - Encoding to use for control connection. UTF-8 by default. Use "latin1" for older servers. - */ - constructor(timeout = 0, encoding = "utf8") { - this.timeout = timeout; - /** Debug-level logging of all socket communication. */ - this.verbose = false; - /** IP version to prefer (4: IPv4, 6: IPv6, undefined: automatic). */ - this.ipFamily = undefined; - /** Options for TLS connections. */ - this.tlsOptions = {}; - /** A multiline response might be received as multiple chunks. */ - this._partialResponse = ""; - this._encoding = encoding; - // Help Typescript understand that we do indeed set _socket in the constructor but use the setter method to do so. - this._socket = this.socket = this._newSocket(); - this._dataSocket = undefined; +function safeCall(fn, input, callback) { + // Replace the callback function with a wrapper that ensures it will only be called once + callback = callOnce(callback); + try { + fn(input, callback); } - /** - * Close the context. - */ - close() { - // Internally, closing a context is always described with an error. If there is still a task running, it will - // abort with an exception that the user closed the client during a task. If no task is running, no exception is - // thrown but all newly submitted tasks after that will abort the exception that the client has been closed. - // In addition the user will get a stack trace pointing to where exactly the client has been closed. So in any - // case use _closingError to determine whether a context is closed. This also allows us to have a single code-path - // for closing a context making the implementation easier. - const message = this._task ? "User closed client during task" : "User closed client"; - const err = new Error(message); - this.closeWithError(err); + catch (err) { + callback(err, undefined); } - /** - * Close the context with an error. - */ - closeWithError(err) { - // If this context already has been closed, don't overwrite the reason. - if (this._closingError) { - return; +} +exports.safeCall = safeCall; +/** + * Returns a wrapper function that ensures the given callback function is only called once. + * Subsequent calls are ignored, unless the first argument is an Error, in which case the + * error is thrown. + * + * @param callback - The function that should only be called once + * + * @internal + */ +function callOnce(callback) { + let fulfilled = false; + return function onceWrapper(err, result) { + if (!fulfilled) { + fulfilled = true; + callback.call(this, err, result); } - this._closingError = err; - // Before giving the user's task a chance to react, make sure we won't be bothered with any inputs. - this._closeSocket(this._socket); - this._closeSocket(this._dataSocket); - // Give the user's task a chance to react, maybe cleanup resources. - this._passToHandler(err); - // The task might not have been rejected by the user after receiving the error. - this._stopTrackingTask(); - } + else if (err) { + // The callback has already been called, but now an error has occurred + // (most likely inside the callback function). So re-throw the error, + // so it gets handled further up the call stack + throw err; + } + }; +} +exports.callOnce = callOnce; +//# sourceMappingURL=call.js.map + +/***/ }), + +/***/ 4918: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_12979__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.DirectoryReader = void 0; +const path = __nested_webpack_require_12979__(5622); +const stream_1 = __nested_webpack_require_12979__(2413); +const call_1 = __nested_webpack_require_12979__(8188); +const normalize_options_1 = __nested_webpack_require_12979__(2977); +const stat_1 = __nested_webpack_require_12979__(9445); +/** + * Asynchronously reads the contents of a directory and streams the results + * via a `ReadableStream`. + * + * @internal + */ +class DirectoryReader { /** - * Returns true if this context has been closed or hasn't been connected yet. You can reopen it with `access`. + * @param dir - The absolute or relative directory path to read + * @param [options] - User-specified options, if any (see `normalizeOptions()`) + * @param facade - sync or async function implementations + * @param emit - Indicates whether the reader should emit "file", "directory", and "symlink" events. */ - get closed() { - return this.socket.remoteAddress === undefined || this._closingError !== undefined; - } - /** - * Reset this contex and all of its state. - */ - reset() { - this.socket = this._newSocket(); - } - /** - * Get the FTP control socket. - */ - get socket() { - return this._socket; - } - /** - * Set the socket for the control connection. This will only close the current control socket - * if the new one is not an upgrade to the current one. - */ - set socket(socket) { - // No data socket should be open in any case where the control socket is set or upgraded. - this.dataSocket = undefined; - this.tlsOptions = {}; - // This being a soft reset, remove any remaining partial response. - this._partialResponse = ""; - if (this._socket) { - // Only close the current connection if the new is not an upgrade. - const isUpgrade = socket.localPort === this._socket.localPort; - if (!isUpgrade) { - this._socket.destroy(); + constructor(dir, options, facade, emit = false) { + this.options = normalize_options_1.normalizeOptions(options, facade, emit); + // Indicates whether we should keep reading + // This is set false if stream.Readable.push() returns false. + this.shouldRead = true; + // The directories to read + // (initialized with the top-level directory) + this.queue = [{ + path: dir, + basePath: this.options.basePath, + depth: 0 + }]; + // The number of directories that are currently being processed + this.pending = 0; + // The data that has been read, but not yet emitted + this.buffer = []; + this.stream = new stream_1.Readable({ objectMode: true }); + this.stream._read = () => { + // Start (or resume) reading + this.shouldRead = true; + // If we have data in the buffer, then send the next chunk + if (this.buffer.length > 0) { + this.pushFromBuffer(); } - this._removeSocketListeners(this._socket); - } - if (socket) { - // Setting a completely new control socket is in essence something like a reset. That's - // why we also close any open data connection above. We can go one step further and reset - // a possible closing error. That means that a closed FTPContext can be "reopened" by - // setting a new control socket. - this._closingError = undefined; - // Don't set a timeout yet. Timeout for control sockets is only active during a task, see handle() below. - socket.setTimeout(0); - socket.setEncoding(this._encoding); - socket.setKeepAlive(true); - socket.on("data", data => this._onControlSocketData(data)); - // Server sending a FIN packet is treated as an error. - socket.on("end", () => this.closeWithError(new Error("Server sent FIN packet unexpectedly, closing connection."))); - // Control being closed without error by server is treated as an error. - socket.on("close", hadError => { if (!hadError) - this.closeWithError(new Error("Server closed connection unexpectedly.")); }); - this._setupDefaultErrorHandlers(socket, "control socket"); - } - this._socket = socket; + // If we have directories queued, then start processing the next one + if (this.queue.length > 0) { + this.readNextDirectory(); + } + this.checkForEOF(); + }; } /** - * Get the current FTP data connection if present. + * Reads the next directory in the queue */ - get dataSocket() { - return this._dataSocket; + readNextDirectory() { + let { facade } = this.options; + let dir = this.queue.shift(); + this.pending++; + // Read the directory listing + call_1.safeCall(facade.fs.readdir, dir.path, (err, items) => { + if (err) { + // fs.readdir threw an error + this.emit("error", err); + return this.finishedReadingDirectory(); + } + try { + // Process each item in the directory (simultaneously, if async) + facade.forEach(items, this.processItem.bind(this, dir), this.finishedReadingDirectory.bind(this, dir)); + } + catch (err2) { + // facade.forEach threw an error + // (probably because fs.readdir returned an invalid result) + this.emit("error", err2); + this.finishedReadingDirectory(); + } + }); } /** - * Set the socket for the data connection. This will automatically close the former data socket. - */ - set dataSocket(socket) { - this._closeSocket(this._dataSocket); - if (socket) { - // Don't set a timeout yet. Timeout data socket should be activated when data transmission starts - // and timeout on control socket is deactivated. - socket.setTimeout(0); - this._setupDefaultErrorHandlers(socket, "data socket"); - } - this._dataSocket = socket; - } - /** - * Get the currently used encoding. - */ - get encoding() { - return this._encoding; - } - /** - * Set the encoding used for the control socket. + * This method is called after all items in a directory have been processed. * - * See https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings for what encodings - * are supported by Node. + * NOTE: This does not necessarily mean that the reader is finished, since there may still + * be other directories queued or pending. */ - set encoding(encoding) { - this._encoding = encoding; - if (this.socket) { - this.socket.setEncoding(encoding); + finishedReadingDirectory() { + this.pending--; + if (this.shouldRead) { + // If we have directories queued, then start processing the next one + if (this.queue.length > 0) { + this.readNextDirectory(); + } + this.checkForEOF(); } } /** - * Send an FTP command without waiting for or handling the result. + * Determines whether the reader has finished processing all items in all directories. + * If so, then the "end" event is fired (via {@Readable#push}) */ - send(command) { - const containsPassword = command.startsWith("PASS"); - const message = containsPassword ? "> PASS ###" : `> ${command}`; - this.log(message); - this._socket.write(command + "\r\n", this.encoding); + checkForEOF() { + if (this.buffer.length === 0 && // The stuff we've already read + this.pending === 0 && // The stuff we're currently reading + this.queue.length === 0) { // The stuff we haven't read yet + // There's no more stuff! + this.stream.push(null); + } } /** - * Send an FTP command and handle the first response. Use this if you have a simple - * request-response situation. + * Processes a single item in a directory. + * + * If the item is a directory, and `option.deep` is enabled, then the item will be added + * to the directory queue. + * + * If the item meets the filter criteria, then it will be emitted to the reader's stream. + * + * @param dir - A directory object from the queue + * @param item - The name of the item (name only, no path) + * @param done - A callback function that is called after the item has been processed */ - request(command) { - return this.handle(command, (res, task) => { - if (res instanceof Error) { - task.reject(res); + processItem(dir, item, done) { + let stream = this.stream; + let options = this.options; + let itemPath = dir.basePath + item; + let fullPath = path.join(dir.path, item); + // If `options.deep` is a number, and we've already recursed to the max depth, + // then there's no need to check fs.Stats to know if it's a directory. + // If `options.deep` is a function, then we'll need fs.Stats + let maxDepthReached = dir.depth >= options.recurseDepth; + // Do we need to call `fs.stat`? + let needStats = !maxDepthReached || // we need the fs.Stats to know if it's a directory + options.stats || // the user wants fs.Stats objects returned + options.recurseFnNeedsStats || // we need fs.Stats for the recurse function + options.filterFnNeedsStats || // we need fs.Stats for the filter function + stream.listenerCount("file") || // we need the fs.Stats to know if it's a file + stream.listenerCount("directory") || // we need the fs.Stats to know if it's a directory + stream.listenerCount("symlink"); // we need the fs.Stats to know if it's a symlink + // If we don't need stats, then exit early + if (!needStats) { + if (this.filter({ path: itemPath })) { + this.pushOrBuffer({ data: itemPath }); + } + return done(); + } + // Get the fs.Stats object for this path + stat_1.stat(options.facade.fs, fullPath, (err, stats) => { + if (err) { + // fs.stat threw an error + this.emit("error", err); + return done(); + } + try { + // Add the item's path to the fs.Stats object + // The base of this path, and its separators are determined by the options + // (i.e. options.basePath and options.sep) + stats.path = itemPath; + // Add depth of the path to the fs.Stats object for use this in the filter function + stats.depth = dir.depth; + if (this.shouldRecurse(stats, maxDepthReached)) { + // Add this subdirectory to the queue + this.queue.push({ + path: fullPath, + basePath: itemPath + options.sep, + depth: dir.depth + 1, + }); + } + // Determine whether this item matches the filter criteria + if (this.filter(stats)) { + this.pushOrBuffer({ + data: options.stats ? stats : itemPath, + file: stats.isFile(), + directory: stats.isDirectory(), + symlink: stats.isSymbolicLink(), + }); + } + done(); + } + catch (err2) { + // An error occurred while processing the item + // (probably during a user-specified function, such as options.deep, options.filter, etc.) + this.emit("error", err2); + done(); + } + }); + } + /** + * Pushes the given chunk of data to the stream, or adds it to the buffer, + * depending on the state of the stream. + */ + pushOrBuffer(chunk) { + // Add the chunk to the buffer + this.buffer.push(chunk); + // If we're still reading, then immediately emit the next chunk in the buffer + // (which may or may not be the chunk that we just added) + if (this.shouldRead) { + this.pushFromBuffer(); + } + } + /** + * Immediately pushes the next chunk in the buffer to the reader's stream. + * The "data" event will always be fired (via `Readable.push()`). + * In addition, the "file", "directory", and/or "symlink" events may be fired, + * depending on the type of properties of the chunk. + */ + pushFromBuffer() { + let stream = this.stream; + let chunk = this.buffer.shift(); + // Stream the data + try { + this.shouldRead = stream.push(chunk.data); + } + catch (err) { + this.emit("error", err); + } + if (this.options.emit) { + // Also emit specific events, based on the type of chunk + chunk.file && this.emit("file", chunk.data); + chunk.symlink && this.emit("symlink", chunk.data); + chunk.directory && this.emit("directory", chunk.data); + } + } + /** + * Determines whether the given directory meets the user-specified recursion criteria. + * If the user didn't specify recursion criteria, then this function will default to true. + * + * @param stats - The directory's `Stats` object + * @param maxDepthReached - Whether we've already crawled the user-specified depth + */ + shouldRecurse(stats, maxDepthReached) { + let { recurseFn } = this.options; + if (maxDepthReached) { + // We've already crawled to the maximum depth. So no more recursion. + return false; + } + else if (!stats.isDirectory()) { + // It's not a directory. So don't try to crawl it. + return false; + } + else if (recurseFn) { + try { + // Run the user-specified recursion criteria + return !!recurseFn(stats); + } + catch (err) { + // An error occurred in the user's code. + // In Sync and Async modes, this will return an error. + // In Streaming mode, we emit an "error" event, but continue processing + this.emit("error", err); + } + } + else { + // No recursion function was specified, and we're within the maximum depth. + // So crawl this directory. + return true; + } + } + /** + * Determines whether the given item meets the user-specified filter criteria. + * If the user didn't specify a filter, then this function will always return true. + * + * @param stats - The item's `Stats` object, or an object with just a `path` property + */ + filter(stats) { + let { filterFn } = this.options; + if (filterFn) { + try { + // Run the user-specified filter function + return !!filterFn(stats); + } + catch (err) { + // An error occurred in the user's code. + // In Sync and Async modes, this will return an error. + // In Streaming mode, we emit an "error" event, but continue processing + this.emit("error", err); + } + } + else { + // No filter was specified, so match everything + return true; + } + } + /** + * Emits an event. If one of the event listeners throws an error, + * then an "error" event is emitted. + */ + emit(eventName, data) { + let stream = this.stream; + try { + stream.emit(eventName, data); + } + catch (err) { + if (eventName === "error") { + // Don't recursively emit "error" events. + // If the first one fails, then just throw + throw err; } else { - task.resolve(res); + stream.emit("error", err); } - }); - } - /** - * Send an FTP command and handle any response until you resolve/reject. Use this if you expect multiple responses - * to a request. This returns a Promise that will hold whatever the response handler passed on when resolving/rejecting its task. - */ - handle(command, responseHandler) { - if (this._task) { - // The user or client instance called `handle()` while a task is still running. - const err = new Error("User launched a task while another one is still running. Forgot to use 'await' or '.then()'?"); - err.stack += `\nRunning task launched at: ${this._task.stack}`; - this.closeWithError(err); - // Don't return here, continue with returning the Promise that will then be rejected - // because the context closed already. That way, users will receive an exception where - // they called this method by mistake. } - return new Promise((resolvePromise, rejectPromise) => { - const stack = new Error().stack || "Unknown call stack"; - const resolver = { - resolve: (...args) => { - this._stopTrackingTask(); - resolvePromise(...args); - }, - reject: err => { - this._stopTrackingTask(); - rejectPromise(err); - } - }; - this._task = { - stack, - resolver, - responseHandler - }; - if (this._closingError) { - // This client has been closed. Provide an error that describes this one as being caused - // by `_closingError`, include stack traces for both. - const err = new Error("Client is closed"); // Type 'Error' is not correctly defined, doesn't have 'code'. - err.stack += `\nClosing reason: ${this._closingError.stack}`; - err.code = this._closingError.code !== undefined ? this._closingError.code : "0"; - this._passToHandler(err); - return; - } - // Only track control socket timeout during the lifecycle of a task. This avoids timeouts on idle sockets, - // the default socket behaviour which is not expected by most users. - this.socket.setTimeout(this.timeout); - if (command) { - this.send(command); - } - }); - } - /** - * Log message if set to be verbose. - */ - log(message) { - if (this.verbose) { - // tslint:disable-next-line no-console - console.log(message); - } - } - /** - * Return true if the control socket is using TLS. This does not mean that a session - * has already been negotiated. - */ - get hasTLS() { - return "encrypted" in this._socket; - } - /** - * Removes reference to current task and handler. This won't resolve or reject the task. - * @protected - */ - _stopTrackingTask() { - // Disable timeout on control socket if there is no task active. - this.socket.setTimeout(0); - this._task = undefined; - } - /** - * Handle incoming data on the control socket. The chunk is going to be of type `string` - * because we let `socket` handle encoding with `setEncoding`. - * @protected - */ - _onControlSocketData(chunk) { - this.log(`< ${chunk}`); - // This chunk might complete an earlier partial response. - const completeResponse = this._partialResponse + chunk; - const parsed = parseControlResponse_1.parseControlResponse(completeResponse); - // Remember any incomplete remainder. - this._partialResponse = parsed.rest; - // Each response group is passed along individually. - for (const message of parsed.messages) { - const code = parseInt(message.substr(0, 3), 10); - const response = { code, message }; - const err = code >= 400 ? new FTPError(response) : undefined; - this._passToHandler(err ? err : response); - } - } - /** - * Send the current handler a response. This is usually a control socket response - * or a socket event, like an error or timeout. - * @protected - */ - _passToHandler(response) { - if (this._task) { - this._task.responseHandler(response, this._task.resolver); - } - // Errors other than FTPError always close the client. If there isn't an active task to handle the error, - // the next one submitted will receive it using `_closingError`. - // There is only one edge-case: If there is an FTPError while no task is active, the error will be dropped. - // But that means that the user sent an FTP command with no intention of handling the result. So why should the - // error be handled? Maybe log it at least? Debug logging will already do that and the client stays useable after - // FTPError. So maybe no need to do anything here. - } - /** - * Setup all error handlers for a socket. - * @protected - */ - _setupDefaultErrorHandlers(socket, identifier) { - socket.once("error", error => { - error.message += ` (${identifier})`; - this.closeWithError(error); - }); - socket.once("close", hadError => { - if (hadError) { - this.closeWithError(new Error(`Socket closed due to transmission error (${identifier})`)); - } - }); - socket.once("timeout", () => this.closeWithError(new Error(`Timeout (${identifier})`))); - } - /** - * Close a socket. - * @protected - */ - _closeSocket(socket) { - if (socket) { - socket.destroy(); - this._removeSocketListeners(socket); - } - } - /** - * Remove all default listeners for socket. - * @protected - */ - _removeSocketListeners(socket) { - socket.removeAllListeners(); - // Before Node.js 10.3.0, using `socket.removeAllListeners()` without any name did not work: https://github.com/nodejs/node/issues/20923. - socket.removeAllListeners("timeout"); - socket.removeAllListeners("data"); - socket.removeAllListeners("end"); - socket.removeAllListeners("error"); - socket.removeAllListeners("close"); - socket.removeAllListeners("connect"); - } - /** - * Provide a new socket instance. - * - * Internal use only, replaced for unit tests. - */ - _newSocket() { - return new net_1.Socket(); } } -exports.FTPContext = FTPContext; - +exports.DirectoryReader = DirectoryReader; +//# sourceMappingURL=directory-reader.js.map /***/ }), -/***/ 73: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_16248__) { +/***/ 8811: +/***/ (function(module, exports, __nested_webpack_require_25219__) { "use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.readdir = void 0; +const async_1 = __nested_webpack_require_25219__(5833); +const iterator_1 = __nested_webpack_require_25219__(5944); +const stream_1 = __nested_webpack_require_25219__(5521); +const sync_1 = __nested_webpack_require_25219__(704); +const readdir = async_1.readdirAsync; +exports.readdir = readdir; +readdir.sync = sync_1.readdirSync; +readdir.async = async_1.readdirAsync; +readdir.stream = stream_1.readdirStream; +readdir.iterator = iterator_1.readdirIterator; +var async_2 = __nested_webpack_require_25219__(5833); +Object.defineProperty(exports, "readdirAsync", ({ enumerable: true, get: function () { return async_2.readdirAsync; } })); +var iterator_2 = __nested_webpack_require_25219__(5944); +Object.defineProperty(exports, "readdirIterator", ({ enumerable: true, get: function () { return iterator_2.readdirIterator; } })); +var stream_2 = __nested_webpack_require_25219__(5521); +Object.defineProperty(exports, "readdirStream", ({ enumerable: true, get: function () { return stream_2.readdirStream; } })); +var sync_2 = __nested_webpack_require_25219__(704); +Object.defineProperty(exports, "readdirSync", ({ enumerable: true, get: function () { return sync_2.readdirSync; } })); +__exportStar(__nested_webpack_require_25219__(6299), exports); +exports.default = readdir; +// CommonJS default export hack +/* eslint-env commonjs */ +if ( true && typeof module.exports === "object") { + module.exports = Object.assign(module.exports.default, module.exports); +} +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ 5944: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_27269__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.readdirIterator = void 0; +const fs = __nested_webpack_require_27269__(5747); +const for_each_1 = __nested_webpack_require_27269__(504); +const directory_reader_1 = __nested_webpack_require_27269__(4918); +const pending_1 = __nested_webpack_require_27269__(8553); +const iteratorFacade = { fs, forEach: for_each_1.asyncForEach }; +function readdirIterator(dir, options) { + let reader = new directory_reader_1.DirectoryReader(dir, options, iteratorFacade); + let stream = reader.stream; + let pendingValues = []; + let pendingReads = []; + let error; + let readable = false; + let done = false; + stream.on("error", function streamError(err) { + error = err; + stream.pause(); + fulfillPendingReads(); + }); + stream.on("end", function streamEnd() { + done = true; + fulfillPendingReads(); + }); + stream.on("readable", function streamReadable() { + readable = true; + fulfillPendingReads(); + }); + return { + [Symbol.asyncIterator]() { + return this; + }, + next() { + let pendingRead = pending_1.pending(); + pendingReads.push(pendingRead); + // eslint-disable-next-line @typescript-eslint/no-floating-promises + Promise.resolve().then(fulfillPendingReads); + return pendingRead.promise; + } + }; + function fulfillPendingReads() { + if (error) { + while (pendingReads.length > 0) { + let pendingRead = pendingReads.shift(); + pendingRead.reject(error); + } + } + else if (pendingReads.length > 0) { + while (pendingReads.length > 0) { + let pendingRead = pendingReads.shift(); + let value = getNextValue(); + if (value) { + pendingRead.resolve({ value }); + } + else if (done) { + pendingRead.resolve({ done, value }); + } + else { + pendingReads.unshift(pendingRead); + break; + } + } + } + } + function getNextValue() { + let value = pendingValues.shift(); + if (value) { + return value; + } + else if (readable) { + readable = false; + while (true) { + value = stream.read(); + if (value) { + pendingValues.push(value); + } + else { + break; + } + } + return pendingValues.shift(); + } + } +} +exports.readdirIterator = readdirIterator; +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ 8553: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.pending = void 0; +/** + * Returns a `Promise` and the functions to resolve or reject it. + * @internal + */ +function pending() { + let resolve, reject; + let promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { + promise, + resolve(result) { + // eslint-disable-next-line @typescript-eslint/no-floating-promises + Promise.resolve(result).then(resolve); + }, + reject(reason) { + Promise.reject(reason).catch(reject); + } + }; +} +exports.pending = pending; +//# sourceMappingURL=pending.js.map + +/***/ }), + +/***/ 2977: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_30859__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.normalizeOptions = void 0; +const file_path_filter_1 = __nested_webpack_require_30859__(3410); +const path = __nested_webpack_require_30859__(5622); +/** + * Validates and normalizes the options argument + * + * @param [options] - User-specified options, if any + * @param facade - sync or async function implementations + * @param emit - Indicates whether the reader should emit "file", "directory", and "symlink" events. + * + * @internal + */ +function normalizeOptions(options, facade, emit) { + if (options === null || options === undefined) { + options = {}; + } + else if (typeof options !== "object") { + throw new TypeError("options must be an object"); + } + let sep = options.sep; + if (sep === null || sep === undefined) { + sep = path.sep; + } + else if (typeof sep !== "string") { + throw new TypeError("options.sep must be a string"); + } + let stats = Boolean(options.stats || options.withFileTypes); + let recurseDepth, recurseFn, recurseFnNeedsStats = false, deep = options.deep; + if (deep === null || deep === undefined) { + recurseDepth = 0; + } + else if (typeof deep === "boolean") { + recurseDepth = deep ? Infinity : 0; + } + else if (typeof deep === "number") { + if (deep < 0 || isNaN(deep)) { + throw new Error("options.deep must be a positive number"); + } + else if (Math.floor(deep) !== deep) { + throw new Error("options.deep must be an integer"); + } + else { + recurseDepth = deep; + } + } + else if (typeof deep === "function") { + // Recursion functions require a Stats object + recurseFnNeedsStats = true; + recurseDepth = Infinity; + recurseFn = deep; + } + else if (deep instanceof RegExp || (typeof deep === "string" && deep.length > 0)) { + recurseDepth = Infinity; + recurseFn = file_path_filter_1.createFilter({ map, sep }, deep); + } + else { + throw new TypeError("options.deep must be a boolean, number, function, regular expression, or glob pattern"); + } + let filterFn, filterFnNeedsStats = false, filter = options.filter; + if (filter !== null && filter !== undefined) { + if (typeof filter === "function") { + // Filter functions requres a Stats object + filterFnNeedsStats = true; + filterFn = filter; + } + else if (filter instanceof RegExp || + typeof filter === "boolean" || + (typeof filter === "string" && filter.length > 0)) { + filterFn = file_path_filter_1.createFilter({ map, sep }, filter); + } + else { + throw new TypeError("options.filter must be a boolean, function, regular expression, or glob pattern"); + } + } + let basePath = options.basePath; + if (basePath === null || basePath === undefined) { + basePath = ""; + } + else if (typeof basePath === "string") { + // Append a path separator to the basePath, if necessary + if (basePath && basePath.substr(-1) !== sep) { + basePath += sep; + } + } + else { + throw new TypeError("options.basePath must be a string"); + } + // Determine which facade methods to use + if (options.fs === null || options.fs === undefined) { + // The user didn't provide their own facades, so use our internal ones + } + else if (typeof options.fs === "object") { + // Merge the internal facade methods with the user-provided `fs` facades + facade = Object.assign({}, facade); + facade.fs = Object.assign({}, facade.fs, options.fs); + } + else { + throw new TypeError("options.fs must be an object"); + } + return { + recurseDepth, + recurseFn, + recurseFnNeedsStats, + filterFn, + filterFnNeedsStats, + stats, + sep, + basePath, + facade, + emit, + }; +} +exports.normalizeOptions = normalizeOptions; +/** + * Maps our modified fs.Stats objects to file paths + */ +function map(stats) { + return stats.path; +} +//# sourceMappingURL=normalize-options.js.map + +/***/ }), + +/***/ 9445: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_35117__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.stat = void 0; +const call_1 = __nested_webpack_require_35117__(8188); +/** + * Retrieves the `Stats` for the given path. If the path is a symbolic link, + * then the Stats of the symlink's target are returned instead. If the symlink is broken, + * then the Stats of the symlink itself are returned. + * + * @param fs - Synchronous or Asynchronouse facade for the "fs" module + * @param path - The path to return stats for + * + * @internal + */ +function stat(fs, path, callback) { + let isSymLink = false; + call_1.safeCall(fs.lstat, path, (err, lstats) => { + if (err) { + // fs.lstat threw an eror + return callback(err, undefined); + } + try { + isSymLink = lstats.isSymbolicLink(); + } + catch (err2) { + // lstats.isSymbolicLink() threw an error + // (probably because fs.lstat returned an invalid result) + return callback(err2, undefined); + } + if (isSymLink) { + // Try to resolve the symlink + symlinkStat(fs, path, lstats, callback); + } + else { + // It's not a symlink, so return the stats as-is + callback(null, lstats); + } + }); +} +exports.stat = stat; +/** + * Retrieves the `Stats` for the target of the given symlink. + * If the symlink is broken, then the Stats of the symlink itself are returned. + * + * @param fs - Synchronous or Asynchronouse facade for the "fs" module + * @param path - The path of the symlink to return stats for + * @param lstats - The stats of the symlink + */ +function symlinkStat(fs, path, lstats, callback) { + call_1.safeCall(fs.stat, path, (err, stats) => { + if (err) { + // The symlink is broken, so return the stats for the link itself + return callback(null, lstats); + } + try { + // Return the stats for the resolved symlink target, + // and override the `isSymbolicLink` method to indicate that it's a symlink + stats.isSymbolicLink = () => true; + } + catch (err2) { + // Setting stats.isSymbolicLink threw an error + // (probably because fs.stat returned an invalid result) + return callback(err2, undefined); + } + callback(null, stats); + }); +} +//# sourceMappingURL=stat.js.map + +/***/ }), + +/***/ 5521: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_37598__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.readdirStream = void 0; +const fs = __nested_webpack_require_37598__(5747); +const for_each_1 = __nested_webpack_require_37598__(504); +const directory_reader_1 = __nested_webpack_require_37598__(4918); +const streamFacade = { fs, forEach: for_each_1.asyncForEach }; +function readdirStream(dir, options) { + let reader = new directory_reader_1.DirectoryReader(dir, options, streamFacade, true); + return reader.stream; +} +exports.readdirStream = readdirStream; +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ 7448: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.syncForEach = void 0; +/** + * A facade that allows `Array.forEach()` to be called as though it were asynchronous. + * + * @param array - The array to iterate over + * @param iterator - The function to call for each item in the array + * @param done - The function to call when all iterators have completed + * + * @internal + */ +function syncForEach(array, iterator, done) { + if (!Array.isArray(array)) { + throw new TypeError(`${array} is not an array`); + } + for (let item of array) { + iterator(item, () => { + // Note: No error-handling here because this is currently only ever called + // by DirectoryReader, which never passes an `error` parameter to the callback. + // Instead, DirectoryReader emits an "error" event if an error occurs. + }); + } + done(); +} +exports.syncForEach = syncForEach; +//# sourceMappingURL=for-each.js.map + +/***/ }), + +/***/ 3073: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_39284__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); exports.syncFS = void 0; -const fs = __nested_webpack_require_16248__(747); -const call_1 = __nested_webpack_require_16248__(188); +const fs = __nested_webpack_require_39284__(5747); +const call_1 = __nested_webpack_require_39284__(8188); /** * Synchronous versions of `fs` methods. * @@ -603,1253 +1548,189 @@ exports.syncFS = { /***/ }), -/***/ 77: -/***/ (function(__unusedmodule, exports) { +/***/ 704: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_40968__) => { "use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ErrorCode = exports.syncFileDescription = exports.currentVersion = void 0; -exports.currentVersion = "1.0.0"; -exports.syncFileDescription = "DO NOT DELETE THIS FILE. This file is used to keep track of which files have been synced in the most recent deployment. If you delete this file a resync will need to be done (which can take a while) - read more: https://github.com/SamKirkland/FTP-Deploy-Action"; -var ErrorCode; -(function (ErrorCode) { - // The requested action is being initiated, expect another reply before proceeding with a new command. - ErrorCode[ErrorCode["RestartMarkerReplay"] = 110] = "RestartMarkerReplay"; - ErrorCode[ErrorCode["ServiceReadyInNNNMinutes"] = 120] = "ServiceReadyInNNNMinutes"; - ErrorCode[ErrorCode["DataConnectionAlreadyOpenStartingTransfer"] = 125] = "DataConnectionAlreadyOpenStartingTransfer"; - ErrorCode[ErrorCode["FileStatusOkayOpeningDataConnection"] = 150] = "FileStatusOkayOpeningDataConnection"; - // The requested action has been successfully completed. - ErrorCode[ErrorCode["CommandNotImplemented"] = 202] = "CommandNotImplemented"; - ErrorCode[ErrorCode["SystemStatus"] = 211] = "SystemStatus"; - ErrorCode[ErrorCode["DirectoryStatus"] = 212] = "DirectoryStatus"; - ErrorCode[ErrorCode["FileStatus"] = 213] = "FileStatus"; - ErrorCode[ErrorCode["HelpMessage"] = 214] = "HelpMessage"; - ErrorCode[ErrorCode["IANAOfficialName"] = 215] = "IANAOfficialName"; - ErrorCode[ErrorCode["ReadyForNewUser"] = 220] = "ReadyForNewUser"; - ErrorCode[ErrorCode["ClosingControlConnection"] = 221] = "ClosingControlConnection"; - ErrorCode[ErrorCode["DataConnectionOpen"] = 225] = "DataConnectionOpen"; - ErrorCode[ErrorCode["SuccessNowClosingDataConnection"] = 226] = "SuccessNowClosingDataConnection"; - ErrorCode[ErrorCode["EnteringPassiveMode"] = 227] = "EnteringPassiveMode"; - ErrorCode[ErrorCode["EnteringLongPassiveMode"] = 228] = "EnteringLongPassiveMode"; - ErrorCode[ErrorCode["EnteringExtendedPassiveMode"] = 229] = "EnteringExtendedPassiveMode"; - ErrorCode[ErrorCode["UserLoggedIn"] = 230] = "UserLoggedIn"; - ErrorCode[ErrorCode["UserLoggedOut"] = 231] = "UserLoggedOut"; - ErrorCode[ErrorCode["LogoutWillCompleteWhenTransferDone"] = 232] = "LogoutWillCompleteWhenTransferDone"; - ErrorCode[ErrorCode["ServerAcceptsAuthenticationMethod"] = 234] = "ServerAcceptsAuthenticationMethod"; - ErrorCode[ErrorCode["ActionComplete"] = 250] = "ActionComplete"; - ErrorCode[ErrorCode["PathNameCreated"] = 257] = "PathNameCreated"; - // The command has been accepted, but the requested action is on hold, pending receipt of further information. - ErrorCode[ErrorCode["UsernameOkayPasswordNeeded"] = 331] = "UsernameOkayPasswordNeeded"; - ErrorCode[ErrorCode["NeedAccountForLogin"] = 332] = "NeedAccountForLogin"; - ErrorCode[ErrorCode["RequestedFileActionPendingFurtherInformation"] = 350] = "RequestedFileActionPendingFurtherInformation"; - // The command was not accepted and the requested action did not take place, but the error condition is temporary and the action may be requested again. - ErrorCode[ErrorCode["ServiceNotAvailable"] = 421] = "ServiceNotAvailable"; - ErrorCode[ErrorCode["CantOpenDataConnection"] = 425] = "CantOpenDataConnection"; - ErrorCode[ErrorCode["ConnectionClosed"] = 426] = "ConnectionClosed"; - ErrorCode[ErrorCode["InvalidUsernameOrPassword"] = 430] = "InvalidUsernameOrPassword"; - ErrorCode[ErrorCode["HostUnavailable"] = 434] = "HostUnavailable"; - ErrorCode[ErrorCode["FileActionNotTaken"] = 450] = "FileActionNotTaken"; - ErrorCode[ErrorCode["LocalErrorProcessing"] = 451] = "LocalErrorProcessing"; - ErrorCode[ErrorCode["InsufficientStorageSpaceOrFileInUse"] = 452] = "InsufficientStorageSpaceOrFileInUse"; - // Syntax error, command unrecognized and the requested action did not take place. This may include errors such as command line too long. - ErrorCode[ErrorCode["SyntaxErrorInParameters"] = 501] = "SyntaxErrorInParameters"; - ErrorCode[ErrorCode["CommandNotImpemented"] = 502] = "CommandNotImpemented"; - ErrorCode[ErrorCode["BadSequenceOfCommands"] = 503] = "BadSequenceOfCommands"; - ErrorCode[ErrorCode["CommandNotImplementedForThatParameter"] = 504] = "CommandNotImplementedForThatParameter"; - ErrorCode[ErrorCode["NotLoggedIn"] = 530] = "NotLoggedIn"; - ErrorCode[ErrorCode["NeedAccountForStoringFiles"] = 532] = "NeedAccountForStoringFiles"; - ErrorCode[ErrorCode["CouldNotConnectToServerRequiresSSL"] = 534] = "CouldNotConnectToServerRequiresSSL"; - ErrorCode[ErrorCode["FileNotFoundOrNoAccess"] = 550] = "FileNotFoundOrNoAccess"; - ErrorCode[ErrorCode["UnknownPageType"] = 551] = "UnknownPageType"; - ErrorCode[ErrorCode["ExceededStorageAllocation"] = 552] = "ExceededStorageAllocation"; - ErrorCode[ErrorCode["FileNameNotAllowed"] = 553] = "FileNameNotAllowed"; - // Replies regarding confidentiality and integrity - ErrorCode[ErrorCode["IntegrityProtectedReply"] = 631] = "IntegrityProtectedReply"; - ErrorCode[ErrorCode["ConfidentialityAndIntegrityProtectedReply"] = 632] = "ConfidentialityAndIntegrityProtectedReply"; - ErrorCode[ErrorCode["ConfidentialityProtectedReply"] = 633] = "ConfidentialityProtectedReply"; - // Common Winsock Error Codes[2] (These are not FTP return codes) - ErrorCode[ErrorCode["ConnectionClosedByServer"] = 10054] = "ConnectionClosedByServer"; - ErrorCode[ErrorCode["CannotConnect"] = 10060] = "CannotConnect"; - ErrorCode[ErrorCode["CannotConnectRefusedByServer"] = 10061] = "CannotConnectRefusedByServer"; - ErrorCode[ErrorCode["DirectoryNotEmpty"] = 10066] = "DirectoryNotEmpty"; - ErrorCode[ErrorCode["TooManyUsers"] = 10068] = "TooManyUsers"; -})(ErrorCode = exports.ErrorCode || (exports.ErrorCode = {})); -; - +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.readdirSync = void 0; +const directory_reader_1 = __nested_webpack_require_40968__(4918); +const for_each_1 = __nested_webpack_require_40968__(7448); +const fs_1 = __nested_webpack_require_40968__(3073); +const syncFacade = { fs: fs_1.syncFS, forEach: for_each_1.syncForEach }; +function readdirSync(dir, options) { + let reader = new directory_reader_1.DirectoryReader(dir, options, syncFacade); + let stream = reader.stream; + let results = []; + let data = stream.read(); + while (data !== null) { + results.push(data); + data = stream.read(); + } + return results; +} +exports.readdirSync = readdirSync; +//# sourceMappingURL=index.js.map /***/ }), -/***/ 78: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_23902__) { +/***/ 6299: +/***/ ((__unused_webpack_module, exports) => { "use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.prettyError = void 0; -var types_1 = __nested_webpack_require_23902__(77); -function outputOriginalErrorAndExit(logger, error) { - logger.warn("------------------------------------------------------"); - logger.warn("Full Error below"); - logger.warn("------------------------------------------------------"); - logger.warn("Full error:", error); - process.exit(); -} -/** - * Converts a exception to helpful debug info - * @param error exception - */ -function prettyError(logger, args, error) { - logger.all("------------------------------------------------------"); - logger.all("------------------ A error occurred ------------------"); - logger.all("------------------------------------------------------"); - if (typeof error.code === "string") { - var errorCode = error.code; - if (errorCode === "ENOTFOUND") { - logger.warn("The server \"" + args.server + "\" doesn't seem to exist. Do you have a typo?"); - outputOriginalErrorAndExit(logger, error); - } - } - if (typeof error.name === "string") { - var errorName = error.name; - if (errorName.includes("ERR_TLS_CERT_ALTNAME_INVALID")) { - logger.warn("The certificate for \"" + args.server + "\" is likely shared. The host did not place your server on the list of valid domains for this cert."); - logger.warn("This is a common issue with shared hosts. You have a few options:"); - logger.warn(" - Ignore this error by setting security back to loose"); - logger.warn(" - Contact your hosting provider and ask them for your servers hostname"); - outputOriginalErrorAndExit(logger, error); - } - } - var ftpError = error; - if (ftpError.code === types_1.ErrorCode.NotLoggedIn) { - logger.warn("Could not login with the username \"" + args.username + "\" and password \"" + args.password + "\"."); - logger.warn("Make sure you can login with those credentials. If you have a space or a quote in your username or password be sure to escape them!"); - outputOriginalErrorAndExit(logger, error); - } - // unknown error :( - outputOriginalErrorAndExit(logger, error); -} -exports.prettyError = prettyError; - - -/***/ }), - -/***/ 117: -/***/ (function(module) { - -module.exports = function (glob, opts) { - if (typeof glob !== 'string') { - throw new TypeError('Expected a string'); - } - - var str = String(glob); - - // The regexp we are building, as a string. - var reStr = ""; - - // Whether we are matching so called "extended" globs (like bash) and should - // support single character matching, matching ranges of characters, group - // matching, etc. - var extended = opts ? !!opts.extended : false; - - // When globstar is _false_ (default), '/foo/*' is translated a regexp like - // '^\/foo\/.*$' which will match any string beginning with '/foo/' - // When globstar is _true_, '/foo/*' is translated to regexp like - // '^\/foo\/[^/]*$' which will match any string beginning with '/foo/' BUT - // which does not have a '/' to the right of it. - // E.g. with '/foo/*' these will match: '/foo/bar', '/foo/bar.txt' but - // these will not '/foo/bar/baz', '/foo/bar/baz.txt' - // Lastely, when globstar is _true_, '/foo/**' is equivelant to '/foo/*' when - // globstar is _false_ - var globstar = opts ? !!opts.globstar : false; - - // If we are doing extended matching, this boolean is true when we are inside - // a group (eg {*.html,*.js}), and false otherwise. - var inGroup = false; - - // RegExp flags (eg "i" ) to pass in to RegExp constructor. - var flags = opts && typeof( opts.flags ) === "string" ? opts.flags : ""; - - var c; - for (var i = 0, len = str.length; i < len; i++) { - c = str[i]; - - switch (c) { - case "/": - case "$": - case "^": - case "+": - case ".": - case "(": - case ")": - case "=": - case "!": - case "|": - reStr += "\\" + c; - break; - - case "?": - if (extended) { - reStr += "."; - break; - } - - case "[": - case "]": - if (extended) { - reStr += c; - break; - } - - case "{": - if (extended) { - inGroup = true; - reStr += "("; - break; - } - - case "}": - if (extended) { - inGroup = false; - reStr += ")"; - break; - } - - case ",": - if (inGroup) { - reStr += "|"; - break; - } - reStr += "\\" + c; - break; - - case "*": - // Move over all consecutive "*"'s. - // Also store the previous and next characters - var prevChar = str[i - 1]; - var starCount = 1; - while(str[i + 1] === "*") { - starCount++; - i++; - } - var nextChar = str[i + 1]; - - if (!globstar) { - // globstar is disabled, so treat any number of "*" as one - reStr += ".*"; - } else { - // globstar is enabled, so determine if this is a globstar segment - var isGlobstar = starCount > 1 // multiple "*"'s - && (prevChar === "/" || prevChar === undefined) // from the start of the segment - && (nextChar === "/" || nextChar === undefined) // to the end of the segment - - if (isGlobstar) { - // it's a globstar, so match zero or more path segments - reStr += "((?:[^/]*(?:\/|$))*)"; - i++; // move over the "/" - } else { - // it's not a globstar, so only match one path segment - reStr += "([^/]*)"; - } - } - break; - - default: - reStr += c; - } - } - - // When regexp 'g' flag is specified don't - // constrain the regular expression with ^ & $ - if (!flags || !~flags.indexOf('g')) { - reStr = "^" + reStr + "$"; - } - - return new RegExp(reStr, flags); -}; - - -/***/ }), - -/***/ 127: -/***/ (function(module, __unusedexports, __nested_webpack_require_29803__) { - -"use strict"; - -const parseMilliseconds = __nested_webpack_require_29803__(816); - -const pluralize = (word, count) => count === 1 ? word : `${word}s`; - -const SECOND_ROUNDING_EPSILON = 0.0000001; - -module.exports = (milliseconds, options = {}) => { - if (!Number.isFinite(milliseconds)) { - throw new TypeError('Expected a finite number'); - } - - if (options.colonNotation) { - options.compact = false; - options.formatSubMilliseconds = false; - options.separateMilliseconds = false; - options.verbose = false; - } - - if (options.compact) { - options.secondsDecimalDigits = 0; - options.millisecondsDecimalDigits = 0; - } - - const result = []; - - const floorDecimals = (value, decimalDigits) => { - const flooredInterimValue = Math.floor((value * (10 ** decimalDigits)) + SECOND_ROUNDING_EPSILON); - const flooredValue = Math.round(flooredInterimValue) / (10 ** decimalDigits); - return flooredValue.toFixed(decimalDigits); - }; - - const add = (value, long, short, valueString) => { - if ((result.length === 0 || !options.colonNotation) && value === 0 && !(options.colonNotation && short === 'm')) { - return; - } - - valueString = (valueString || value || '0').toString(); - let prefix; - let suffix; - if (options.colonNotation) { - prefix = result.length > 0 ? ':' : ''; - suffix = ''; - const wholeDigits = valueString.includes('.') ? valueString.split('.')[0].length : valueString.length; - const minLength = result.length > 0 ? 2 : 1; - valueString = '0'.repeat(Math.max(0, minLength - wholeDigits)) + valueString; - } else { - prefix = ''; - suffix = options.verbose ? ' ' + pluralize(long, value) : short; - } - - result.push(prefix + valueString + suffix); - }; - - const parsed = parseMilliseconds(milliseconds); - - add(Math.trunc(parsed.days / 365), 'year', 'y'); - add(parsed.days % 365, 'day', 'd'); - add(parsed.hours, 'hour', 'h'); - add(parsed.minutes, 'minute', 'm'); - - if ( - options.separateMilliseconds || - options.formatSubMilliseconds || - milliseconds < 1000 - ) { - add(parsed.seconds, 'second', 's'); - if (options.formatSubMilliseconds) { - add(parsed.milliseconds, 'millisecond', 'ms'); - add(parsed.microseconds, 'microsecond', 'µs'); - add(parsed.nanoseconds, 'nanosecond', 'ns'); - } else { - const millisecondsAndBelow = - parsed.milliseconds + - (parsed.microseconds / 1000) + - (parsed.nanoseconds / 1e6); - - const millisecondsDecimalDigits = - typeof options.millisecondsDecimalDigits === 'number' ? - options.millisecondsDecimalDigits : - 0; - - const roundedMiliseconds = millisecondsAndBelow >= 1 ? - Math.round(millisecondsAndBelow) : - Math.ceil(millisecondsAndBelow); - - const millisecondsString = millisecondsDecimalDigits ? - millisecondsAndBelow.toFixed(millisecondsDecimalDigits) : - roundedMiliseconds; - - add( - Number.parseFloat(millisecondsString, 10), - 'millisecond', - 'ms', - millisecondsString - ); - } - } else { - const seconds = (milliseconds / 1000) % 60; - const secondsDecimalDigits = - typeof options.secondsDecimalDigits === 'number' ? - options.secondsDecimalDigits : - 1; - const secondsFixed = floorDecimals(seconds, secondsDecimalDigits); - const secondsString = options.keepDecimalsOnWholeSeconds ? - secondsFixed : - secondsFixed.replace(/\.0+$/, ''); - add(Number.parseFloat(secondsString, 10), 'second', 's', secondsString); - } - - if (result.length === 0) { - return '0' + (options.verbose ? ' milliseconds' : 'ms'); - } - - if (options.compact) { - return result[0]; - } - - if (typeof options.unitCount === 'number') { - const separator = options.colonNotation ? '' : ' '; - return result.slice(0, Math.max(options.unitCount, 1)).join(separator); - } - - return options.colonNotation ? result.join('') : result.join(' '); -}; - - -/***/ }), - -/***/ 157: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_33594__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.parseMLSxDate = exports.transformList = exports.parseLine = exports.testLine = void 0; -const FileInfo_1 = __nested_webpack_require_33594__(202); -function parseSize(value, info) { - info.size = parseInt(value, 10); -} -/** - * Parsers for MLSD facts. - */ -const factHandlersByName = { - "size": parseSize, - "sizd": parseSize, - "unique": (value, info) => { - info.uniqueID = value; - }, - "modify": (value, info) => { - info.modifiedAt = parseMLSxDate(value); - info.rawModifiedAt = info.modifiedAt.toISOString(); - }, - "type": (value, info) => { - // There seems to be confusion on how to handle symbolic links for Unix. RFC 3659 doesn't describe - // this but mentions some examples using the syntax `type=OS.unix=slink:`. But according to - // an entry in the Errata (https://www.rfc-editor.org/errata/eid1500) this syntax can't be valid. - // Instead it proposes to use `type=OS.unix=symlink` and to then list the actual target of the - // symbolic link as another entry in the directory listing. The unique identifiers can then be used - // to derive the connection between link(s) and target. We'll have to handle both cases as there - // are differing opinions on how to deal with this. Here are some links on this topic: - // - ProFTPD source: https://github.com/proftpd/proftpd/blob/56e6dfa598cbd4ef5c6cba439bcbcd53a63e3b21/modules/mod_facts.c#L531 - // - ProFTPD bug: http://bugs.proftpd.org/show_bug.cgi?id=3318 - // - ProFTPD statement: http://www.proftpd.org/docs/modules/mod_facts.html - // – FileZilla bug: https://trac.filezilla-project.org/ticket/9310 - if (value.startsWith("OS.unix=slink")) { - info.type = FileInfo_1.FileType.SymbolicLink; - info.link = value.substr(value.indexOf(":") + 1); - return 1 /* Continue */; - } - switch (value) { - case "file": - info.type = FileInfo_1.FileType.File; - break; - case "dir": - info.type = FileInfo_1.FileType.Directory; - break; - case "OS.unix=symlink": - info.type = FileInfo_1.FileType.SymbolicLink; - // The target of the symbolic link might be defined in another line in the directory listing. - // We'll handle this in `transformList()` below. - break; - case "cdir": // Current directory being listed - case "pdir": // Parent directory - return 2 /* IgnoreFile */; // Don't include these entries in the listing - default: - info.type = FileInfo_1.FileType.Unknown; - } - return 1 /* Continue */; - }, - "unix.mode": (value, info) => { - const digits = value.substr(-3); - info.permissions = { - user: parseInt(digits[0], 10), - group: parseInt(digits[1], 10), - world: parseInt(digits[2], 10) - }; - }, - "unix.ownername": (value, info) => { - info.user = value; - }, - "unix.owner": (value, info) => { - if (info.user === undefined) - info.user = value; - }, - get "unix.uid"() { - return this["unix.owner"]; - }, - "unix.groupname": (value, info) => { - info.group = value; - }, - "unix.group": (value, info) => { - if (info.group === undefined) - info.group = value; - }, - get "unix.gid"() { - return this["unix.group"]; - } - // Regarding the fact "perm": - // We don't handle permission information stored in "perm" because its information is conceptually - // different from what users of FTP clients usually associate with "permissions". Those that have - // some expectations (and probably want to edit them with a SITE command) often unknowingly expect - // the Unix permission system. The information passed by "perm" describes what FTP commands can be - // executed with a file/directory. But even this can be either incomplete or just meant as a "guide" - // as the spec mentions. From https://tools.ietf.org/html/rfc3659#section-7.5.5: "The permissions are - // described here as they apply to FTP commands. They may not map easily into particular permissions - // available on the server's operating system." The parser by Apache Commons tries to translate these - // to Unix permissions – this is misleading users and might not even be correct. -}; -/** - * Split a string once at the first position of a delimiter. For example - * `splitStringOnce("a b c d", " ")` returns `["a", "b c d"]`. - */ -function splitStringOnce(str, delimiter) { - const pos = str.indexOf(delimiter); - const a = str.substr(0, pos); - const b = str.substr(pos + delimiter.length); - return [a, b]; -} -/** - * Returns true if a given line might be part of an MLSD listing. - * - * - Example 1: `size=15227;type=dir;perm=el;modify=20190419065730; test one` - * - Example 2: ` file name` (leading space) - */ -function testLine(line) { - return /^\S+=\S+;/.test(line) || line.startsWith(" "); -} -exports.testLine = testLine; -/** - * Parse single line as MLSD listing, see specification at https://tools.ietf.org/html/rfc3659#section-7. - */ -function parseLine(line) { - const [packedFacts, name] = splitStringOnce(line, " "); - if (name === "" || name === "." || name === "..") { - return undefined; - } - const info = new FileInfo_1.FileInfo(name); - const facts = packedFacts.split(";"); - for (const fact of facts) { - const [factName, factValue] = splitStringOnce(fact, "="); - if (!factValue) { - continue; - } - const factHandler = factHandlersByName[factName.toLowerCase()]; - if (!factHandler) { - continue; - } - const result = factHandler(factValue, info); - if (result === 2 /* IgnoreFile */) { - return undefined; - } - } - return info; -} -exports.parseLine = parseLine; -function transformList(files) { - // Create a map of all files that are not symbolic links by their unique ID - const nonLinksByID = new Map(); - for (const file of files) { - if (!file.isSymbolicLink && file.uniqueID !== undefined) { - nonLinksByID.set(file.uniqueID, file); - } - } - const resolvedFiles = []; - for (const file of files) { - // Try to associate unresolved symbolic links with a target file/directory. - if (file.isSymbolicLink && file.uniqueID !== undefined && file.link === undefined) { - const target = nonLinksByID.get(file.uniqueID); - if (target !== undefined) { - file.link = target.name; - } - } - // The target of a symbolic link is listed as an entry in the directory listing but might - // have a path pointing outside of this directory. In that case we don't want this entry - // to be part of the listing. We generally don't want these kind of entries at all. - const isPartOfDirectory = !file.name.includes("/"); - if (isPartOfDirectory) { - resolvedFiles.push(file); - } - } - return resolvedFiles; -} -exports.transformList = transformList; -/** - * Parse date as specified in https://tools.ietf.org/html/rfc3659#section-2.3. - * - * Message contains response code and modified time in the format: YYYYMMDDHHMMSS[.sss] - * For example `19991005213102` or `19980615100045.014`. - */ -function parseMLSxDate(fact) { - return new Date(Date.UTC(+fact.slice(0, 4), // Year - +fact.slice(4, 6) - 1, // Month - +fact.slice(6, 8), // Date - +fact.slice(8, 10), // Hours - +fact.slice(10, 12), // Minutes - +fact.slice(12, 14), // Seconds - +fact.slice(15, 18) // Milliseconds - )); -} -exports.parseMLSxDate = parseMLSxDate; - - -/***/ }), - -/***/ 168: -/***/ (function(module) { - -"use strict"; - - -const BYTE_UNITS = [ - 'B', - 'kB', - 'MB', - 'GB', - 'TB', - 'PB', - 'EB', - 'ZB', - 'YB' -]; - -const BIT_UNITS = [ - 'b', - 'kbit', - 'Mbit', - 'Gbit', - 'Tbit', - 'Pbit', - 'Ebit', - 'Zbit', - 'Ybit' -]; - -/* -Formats the given number using `Number#toLocaleString`. -- If locale is a string, the value is expected to be a locale-key (for example: `de`). -- If locale is true, the system default locale is used for translation. -- If no value for locale is specified, the number is returned unmodified. -*/ -const toLocaleString = (number, locale) => { - let result = number; - if (typeof locale === 'string') { - result = number.toLocaleString(locale); - } else if (locale === true) { - result = number.toLocaleString(); - } - - return result; -}; - -module.exports = (number, options) => { - if (!Number.isFinite(number)) { - throw new TypeError(`Expected a finite number, got ${typeof number}: ${number}`); - } - - options = Object.assign({bits: false}, options); - const UNITS = options.bits ? BIT_UNITS : BYTE_UNITS; - - if (options.signed && number === 0) { - return ' 0 ' + UNITS[0]; - } - - const isNegative = number < 0; - const prefix = isNegative ? '-' : (options.signed ? '+' : ''); - - if (isNegative) { - number = -number; - } - - if (number < 1) { - const numberString = toLocaleString(number, options.locale); - return prefix + numberString + ' ' + UNITS[0]; - } - - const exponent = Math.min(Math.floor(Math.log10(number) / 3), UNITS.length - 1); - // eslint-disable-next-line unicorn/prefer-exponentiation-operator - number = Number((number / Math.pow(1000, exponent)).toPrecision(3)); - const numberString = toLocaleString(number, options.locale); - - const unit = UNITS[exponent]; - - return prefix + numberString + ' ' + unit; -}; - - -/***/ }), - -/***/ 170: -/***/ (function(__unusedmodule, exports) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ProgressTracker = void 0; -/** - * Tracks progress of one socket data transfer at a time. - */ -class ProgressTracker { - constructor() { - this.bytesOverall = 0; - this.intervalMs = 500; - this.onStop = noop; - this.onHandle = noop; - } - /** - * Register a new handler for progress info. Use `undefined` to disable reporting. - */ - reportTo(onHandle = noop) { - this.onHandle = onHandle; - } - /** - * Start tracking transfer progress of a socket. - * - * @param socket The socket to observe. - * @param name A name associated with this progress tracking, e.g. a filename. - * @param type The type of the transfer, typically "upload" or "download". - */ - start(socket, name, type) { - let lastBytes = 0; - this.onStop = poll(this.intervalMs, () => { - const bytes = socket.bytesRead + socket.bytesWritten; - this.bytesOverall += bytes - lastBytes; - lastBytes = bytes; - this.onHandle({ - name, - type, - bytes, - bytesOverall: this.bytesOverall - }); - }); - } - /** - * Stop tracking transfer progress. - */ - stop() { - this.onStop(false); - } - /** - * Call the progress handler one more time, then stop tracking. - */ - updateAndStop() { - this.onStop(true); - } -} -exports.ProgressTracker = ProgressTracker; -/** - * Starts calling a callback function at a regular interval. The first call will go out - * immediately. The function returns a function to stop the polling. - */ -function poll(intervalMs, updateFunc) { - const id = setInterval(updateFunc, intervalMs); - const stopFunc = (stopWithUpdate) => { - clearInterval(id); - if (stopWithUpdate) { - updateFunc(); - } - // Prevent repeated calls to stop calling handler. - updateFunc = noop; - }; - updateFunc(); - return stopFunc; -} -function noop() { } - - -/***/ }), - -/***/ 176: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_45426__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.createFilter = void 0; -const normalize_1 = __nested_webpack_require_45426__(561); -const util_1 = __nested_webpack_require_45426__(735); -function createFilter(options, ...args) { - let criteria = args.length <= 1 ? args[0] : args; - let filters = normalize_1.normalize(criteria, options); - pathFilter[util_1._filters] = filters; - return pathFilter; - function pathFilter(...args) { - // Does the file path match any of the exclude filters? - let exclude = filters.exclude.some((filter) => filter(...args)); - if (exclude) { - return false; - } - if (filters.include.length === 0) { - // Include everything that's not excluded - return true; - } - // Does the file path match any of the include filters? - let include = filters.include.some((filter) => filter(...args)); - return include; - } -} -exports.createFilter = createFilter; -//# sourceMappingURL=create-filter.js.map - -/***/ }), - -/***/ 184: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_46544__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.StringWriter = void 0; -const stream_1 = __nested_webpack_require_46544__(413); -class StringWriter extends stream_1.Writable { - constructor() { - super(...arguments); - this.buf = Buffer.alloc(0); - } - _write(chunk, _, callback) { - if (chunk instanceof Buffer) { - this.buf = Buffer.concat([this.buf, chunk]); - callback(null); - } - else { - callback(new Error("StringWriter expects chunks of type 'Buffer'.")); - } - } - getText(encoding) { - return this.buf.toString(encoding); - } -} -exports.StringWriter = StringWriter; - - -/***/ }), - -/***/ 188: -/***/ (function(__unusedmodule, exports) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.callOnce = exports.safeCall = void 0; -/** - * Calls a function with the given arguments, and ensures that the error-first callback is _always_ - * invoked exactly once, even if the function throws an error. - * - * @param fn - The function to invoke - * @param args - The arguments to pass to the function. The final argument must be a callback function. - * - * @internal - */ -function safeCall(fn, input, callback) { - // Replace the callback function with a wrapper that ensures it will only be called once - callback = callOnce(callback); - try { - fn(input, callback); - } - catch (err) { - callback(err, undefined); - } -} -exports.safeCall = safeCall; -/** - * Returns a wrapper function that ensures the given callback function is only called once. - * Subsequent calls are ignored, unless the first argument is an Error, in which case the - * error is thrown. - * - * @param callback - The function that should only be called once - * - * @internal - */ -function callOnce(callback) { - let fulfilled = false; - return function onceWrapper(err, result) { - if (!fulfilled) { - fulfilled = true; - callback.call(this, err, result); - } - else if (err) { - // The callback has already been called, but now an error has occurred - // (most likely inside the callback function). So re-throw the error, - // so it gets handled further up the call stack - throw err; - } - }; -} -exports.callOnce = callOnce; -//# sourceMappingURL=call.js.map - -/***/ }), - -/***/ 199: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_49006__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.transformList = exports.parseLine = exports.testLine = void 0; -const FileInfo_1 = __nested_webpack_require_49006__(202); -/** - * This parser is based on the FTP client library source code in Apache Commons Net provided - * under the Apache 2.0 license. It has been simplified and rewritten to better fit the Javascript language. - * - * https://github.com/apache/commons-net/blob/master/src/main/java/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java - */ -const RE_LINE = new RegExp("(\\S+)\\s+(\\S+)\\s+" // MM-dd-yy whitespace hh:mma|kk:mm swallow trailing spaces - + "(?:()|([0-9]+))\\s+" // or ddddd swallow trailing spaces - + "(\\S.*)" // First non-space followed by rest of line (name) -); -/** - * Returns true if a given line might be a DOS-style listing. - * - * - Example: `12-05-96 05:03PM myDir` - */ -function testLine(line) { - return /^\d{2}/.test(line) && RE_LINE.test(line); -} -exports.testLine = testLine; -/** - * Parse a single line of a DOS-style directory listing. - */ -function parseLine(line) { - const groups = line.match(RE_LINE); - if (groups === null) { - return undefined; - } - const name = groups[5]; - if (name === "." || name === "..") { // Ignore parent directory links - return undefined; - } - const file = new FileInfo_1.FileInfo(name); - const fileType = groups[3]; - if (fileType === "") { - file.type = FileInfo_1.FileType.Directory; - file.size = 0; - } - else { - file.type = FileInfo_1.FileType.File; - file.size = parseInt(groups[4], 10); - } - file.rawModifiedAt = groups[1] + " " + groups[2]; - return file; -} -exports.parseLine = parseLine; -function transformList(files) { - return files; -} -exports.transformList = transformList; - - -/***/ }), - -/***/ 202: -/***/ (function(__unusedmodule, exports) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FileInfo = exports.FileType = void 0; -var FileType; -(function (FileType) { - FileType[FileType["Unknown"] = 0] = "Unknown"; - FileType[FileType["File"] = 1] = "File"; - FileType[FileType["Directory"] = 2] = "Directory"; - FileType[FileType["SymbolicLink"] = 3] = "SymbolicLink"; -})(FileType = exports.FileType || (exports.FileType = {})); -/** - * Describes a file, directory or symbolic link. - */ -class FileInfo { - constructor(name) { - this.name = name; - this.type = FileType.Unknown; - this.size = 0; - /** - * Unparsed, raw modification date as a string. - * - * If `modifiedAt` is undefined, the FTP server you're connected to doesn't support the more modern - * MLSD command for machine-readable directory listings. The older command LIST is then used returning - * results that vary a lot between servers as the format hasn't been standardized. Here, directory listings - * and especially modification dates were meant to be human-readable first. - * - * Be careful when still trying to parse this by yourself. Parsing dates from listings using LIST is - * unreliable. This library decides to offer parsed dates only when they're absolutely reliable and safe to - * use e.g. for comparisons. - */ - this.rawModifiedAt = ""; - /** - * Parsed modification date. - * - * Available if the FTP server supports the MLSD command. Only MLSD guarantees dates than can be reliably - * parsed with the correct timezone and a resolution down to seconds. See `rawModifiedAt` property for the unparsed - * date that is always available. - */ - this.modifiedAt = undefined; - /** - * Unix permissions if present. If the underlying FTP server is not running on Unix this will be undefined. - * If set, you might be able to edit permissions with the FTP command `SITE CHMOD`. - */ - this.permissions = undefined; - /** - * Hard link count if available. - */ - this.hardLinkCount = undefined; - /** - * Link name for symbolic links if available. - */ - this.link = undefined; - /** - * Unix group if available. - */ - this.group = undefined; - /** - * Unix user if available. - */ - this.user = undefined; - /** - * Unique ID if available. - */ - this.uniqueID = undefined; - this.name = name; - } - get isDirectory() { - return this.type === FileType.Directory; - } - get isSymbolicLink() { - return this.type === FileType.SymbolicLink; - } - get isFile() { - return this.type === FileType.File; - } - /** - * Deprecated, legacy API. Use `rawModifiedAt` instead. - * @deprecated - */ - get date() { - return this.rawModifiedAt; - } - set date(rawModifiedAt) { - this.rawModifiedAt = rawModifiedAt; - } -} -exports.FileInfo = FileInfo; -FileInfo.UnixPermission = { - Read: 4, - Write: 2, - Execute: 1 -}; - - -/***/ }), - -/***/ 225: -/***/ (function(__unusedmodule, exports) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -//# sourceMappingURL=types.js.map - -/***/ }), - -/***/ 262: -/***/ (function(__unusedmodule, exports) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.HashDiff = void 0; -function formatNumber(number) { - return number.toLocaleString(); -} -var HashDiff = /** @class */ (function () { - function HashDiff() { - } - HashDiff.prototype.getDiffs = function (localFiles, serverFiles, logger) { - var _a, _b, _c; - var uploadList = []; - var deleteList = []; - var replaceList = []; - var sizeUpload = 0; - var sizeDelete = 0; - var sizeReplace = 0; - // alphabetize each list based off path - var localFilesSorted = localFiles.data.sort(function (first, second) { return first.name.localeCompare(second.name); }); - var serverFilesSorted = serverFiles.data.sort(function (first, second) { return first.name.localeCompare(second.name); }); - logger.info("------------------------------------------------------"); - logger.info("Local Files:\t" + formatNumber(localFilesSorted.length)); - logger.info("Server Files:\t" + formatNumber(localFilesSorted.length)); - logger.info("------------------------------------------------------"); - logger.info("Calculating differences between client & server"); - logger.info("------------------------------------------------------"); - var localPosition = 0; - var serverPosition = 0; - while (localPosition + serverPosition < localFilesSorted.length + serverFilesSorted.length) { - var localFile = localFilesSorted[localPosition]; - var serverFile = serverFilesSorted[serverPosition]; - var fileNameCompare = 0; - if (localFile === undefined) { - fileNameCompare = 1; - } - if (serverFile === undefined) { - fileNameCompare = -1; - } - if (localFile !== undefined && serverFile !== undefined) { - fileNameCompare = localFile.name.localeCompare(serverFile.name); - } - if (fileNameCompare < 0) { - var icon = localFile.type === "folder" ? "\uD83D\uDCC1 Create" : "\u2795 Upload"; - logger.info(icon + ": " + localFile.name); - uploadList.push(localFile); - sizeUpload += (_a = localFile.size) !== null && _a !== void 0 ? _a : 0; - localPosition += 1; - } - else if (fileNameCompare > 0) { - var icon = serverFile.type === "folder" ? "\uD83D\uDCC1" : "\uD83D\uDDD1\uFE0F"; - logger.info(icon + " Delete: " + serverFile.name + " "); - deleteList.push(serverFile); - sizeDelete += (_b = serverFile.size) !== null && _b !== void 0 ? _b : 0; - serverPosition += 1; - } - else if (fileNameCompare === 0) { - // paths are a match - if (localFile.type === "file" && serverFile.type === "file") { - if (localFile.hash === serverFile.hash) { - logger.info("\u2696\uFE0F File content is the same, doing nothing: " + localFile.name); - } - else { - logger.info("\uD83D\uDD01 File replace: " + localFile.name); - sizeReplace += (_c = localFile.size) !== null && _c !== void 0 ? _c : 0; - replaceList.push(localFile); - } - } - localPosition += 1; - serverPosition += 1; - } - } - return { - upload: uploadList, - delete: deleteList, - replace: replaceList, - sizeDelete: sizeDelete, - sizeReplace: sizeReplace, - sizeUpload: sizeUpload - }; - }; - return HashDiff; -}()); -exports.HashDiff = HashDiff; - - -/***/ }), - -/***/ 288: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_58351__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ipIsPrivateV4Address = exports.upgradeSocket = exports.describeAddress = exports.describeTLS = void 0; -const tls_1 = __nested_webpack_require_58351__(16); -/** - * Returns a string describing the encryption on a given socket instance. - */ -function describeTLS(socket) { - if (socket instanceof tls_1.TLSSocket) { - const protocol = socket.getProtocol(); - return protocol ? protocol : "Server socket or disconnected client socket"; - } - return "No encryption"; -} -exports.describeTLS = describeTLS; -/** - * Returns a string describing the remote address of a socket. - */ -function describeAddress(socket) { - if (socket.remoteFamily === "IPv6") { - return `[${socket.remoteAddress}]:${socket.remotePort}`; - } - return `${socket.remoteAddress}:${socket.remotePort}`; -} -exports.describeAddress = describeAddress; -/** - * Upgrade a socket connection with TLS. - */ -function upgradeSocket(socket, options) { - return new Promise((resolve, reject) => { - const tlsOptions = Object.assign({}, options, { - socket - }); - const tlsSocket = tls_1.connect(tlsOptions, () => { - const expectCertificate = tlsOptions.rejectUnauthorized !== false; - if (expectCertificate && !tlsSocket.authorized) { - reject(tlsSocket.authorizationError); - } - else { - // Remove error listener added below. - tlsSocket.removeAllListeners("error"); - resolve(tlsSocket); - } - }).once("error", error => { - reject(error); - }); - }); -} -exports.upgradeSocket = upgradeSocket; -/** - * Returns true if an IP is a private address according to https://tools.ietf.org/html/rfc1918#section-3. - * This will handle IPv4-mapped IPv6 addresses correctly but return false for all other IPv6 addresses. - * - * @param ip The IP as a string, e.g. "192.168.0.1" - */ -function ipIsPrivateV4Address(ip = "") { - // Handle IPv4-mapped IPv6 addresses like ::ffff:192.168.0.1 - if (ip.startsWith("::ffff:")) { - ip = ip.substr(7); // Strip ::ffff: prefix - } - const octets = ip.split(".").map(o => parseInt(o, 10)); - return octets[0] === 10 // 10.0.0.0 - 10.255.255.255 - || (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) // 172.16.0.0 - 172.31.255.255 - || (octets[0] === 192 && octets[1] === 168); // 192.168.0.0 - 192.168.255.255 -} -exports.ipIsPrivateV4Address = ipIsPrivateV4Address; - - -/***/ }), - -/***/ 299: -/***/ (function(__unusedmodule, exports) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); +Object.defineProperty(exports, "__esModule", ({ value: true })); //# sourceMappingURL=types-public.js.map /***/ }), -/***/ 337: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_61163__) { +/***/ 6554: +/***/ ((module) => { "use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); + +const arrayDiffer = (array, ...values) => { + const rest = new Set([].concat(...values)); + return array.filter(element => !rest.has(element)); +}; + +module.exports = arrayDiffer; + + +/***/ }), + +/***/ 9600: +/***/ ((module) => { + +"use strict"; + + +module.exports = (...arguments_) => { + return [...new Set([].concat(...arguments_))]; +}; + + +/***/ }), + +/***/ 1546: +/***/ ((module) => { + +"use strict"; + + +const arrify = value => { + if (value === null || value === undefined) { + return []; + } + + if (Array.isArray(value)) { + return value; + } + + if (typeof value === 'string') { + return [value]; + } + + if (typeof value[Symbol.iterator] === 'function') { + return [...value]; + } + + return [value]; +}; + +module.exports = arrify; + + +/***/ }), + +/***/ 9417: +/***/ ((module) => { + +"use strict"; + +module.exports = balanced; +function balanced(a, b, str) { + if (a instanceof RegExp) a = maybeMatch(a, str); + if (b instanceof RegExp) b = maybeMatch(b, str); + + var r = range(a, b, str); + + return r && { + start: r[0], + end: r[1], + pre: str.slice(0, r[0]), + body: str.slice(r[0] + a.length, r[1]), + post: str.slice(r[1] + b.length) + }; +} + +function maybeMatch(reg, str) { + var m = str.match(reg); + return m ? m[0] : null; +} + +balanced.range = range; +function range(a, b, str) { + var begs, beg, left, right, result; + var ai = str.indexOf(a); + var bi = str.indexOf(b, ai + 1); + var i = ai; + + if (ai >= 0 && bi > 0) { + begs = []; + left = str.length; + + while (i >= 0 && !result) { + if (i == ai) { + begs.push(i); + ai = str.indexOf(a, i + 1); + } else if (begs.length == 1) { + result = [ begs.pop(), bi ]; + } else { + beg = begs.pop(); + if (beg < left) { + left = beg; + right = bi; + } + + bi = str.indexOf(b, i + 1); + } + + i = ai < bi && ai >= 0 ? ai : bi; + } + + if (begs.length) { + result = [ left, right ]; + } + } + + return result; +} + + +/***/ }), + +/***/ 8337: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_43958__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); exports.Client = void 0; -const fs_1 = __nested_webpack_require_61163__(747); -const path_1 = __nested_webpack_require_61163__(622); -const tls_1 = __nested_webpack_require_61163__(16); -const util_1 = __nested_webpack_require_61163__(669); -const FtpContext_1 = __nested_webpack_require_61163__(52); -const parseList_1 = __nested_webpack_require_61163__(993); -const ProgressTracker_1 = __nested_webpack_require_61163__(170); -const StringWriter_1 = __nested_webpack_require_61163__(184); -const parseListMLSD_1 = __nested_webpack_require_61163__(157); -const netUtils_1 = __nested_webpack_require_61163__(288); -const transfer_1 = __nested_webpack_require_61163__(803); -const parseControlResponse_1 = __nested_webpack_require_61163__(948); +const fs_1 = __nested_webpack_require_43958__(5747); +const path_1 = __nested_webpack_require_43958__(5622); +const tls_1 = __nested_webpack_require_43958__(4016); +const util_1 = __nested_webpack_require_43958__(1669); +const FtpContext_1 = __nested_webpack_require_43958__(9052); +const parseList_1 = __nested_webpack_require_43958__(2993); +const ProgressTracker_1 = __nested_webpack_require_43958__(7170); +const StringWriter_1 = __nested_webpack_require_43958__(8184); +const parseListMLSD_1 = __nested_webpack_require_43958__(8157); +const netUtils_1 = __nested_webpack_require_43958__(6288); +const transfer_1 = __nested_webpack_require_43958__(5803); +const parseControlResponse_1 = __nested_webpack_require_43958__(9948); // Use promisify to keep the library compatible with Node 8. const fsReadDir = util_1.promisify(fs_1.readdir); const fsMkDir = util_1.promisify(fs_1.mkdir); @@ -2596,14 +2477,1101 @@ async function ignoreError(func) { /***/ }), -/***/ 375: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_91612__) { +/***/ 202: +/***/ ((__unused_webpack_module, exports) => { "use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.FileInfo = exports.FileType = void 0; +var FileType; +(function (FileType) { + FileType[FileType["Unknown"] = 0] = "Unknown"; + FileType[FileType["File"] = 1] = "File"; + FileType[FileType["Directory"] = 2] = "Directory"; + FileType[FileType["SymbolicLink"] = 3] = "SymbolicLink"; +})(FileType = exports.FileType || (exports.FileType = {})); +/** + * Describes a file, directory or symbolic link. + */ +class FileInfo { + constructor(name) { + this.name = name; + this.type = FileType.Unknown; + this.size = 0; + /** + * Unparsed, raw modification date as a string. + * + * If `modifiedAt` is undefined, the FTP server you're connected to doesn't support the more modern + * MLSD command for machine-readable directory listings. The older command LIST is then used returning + * results that vary a lot between servers as the format hasn't been standardized. Here, directory listings + * and especially modification dates were meant to be human-readable first. + * + * Be careful when still trying to parse this by yourself. Parsing dates from listings using LIST is + * unreliable. This library decides to offer parsed dates only when they're absolutely reliable and safe to + * use e.g. for comparisons. + */ + this.rawModifiedAt = ""; + /** + * Parsed modification date. + * + * Available if the FTP server supports the MLSD command. Only MLSD guarantees dates than can be reliably + * parsed with the correct timezone and a resolution down to seconds. See `rawModifiedAt` property for the unparsed + * date that is always available. + */ + this.modifiedAt = undefined; + /** + * Unix permissions if present. If the underlying FTP server is not running on Unix this will be undefined. + * If set, you might be able to edit permissions with the FTP command `SITE CHMOD`. + */ + this.permissions = undefined; + /** + * Hard link count if available. + */ + this.hardLinkCount = undefined; + /** + * Link name for symbolic links if available. + */ + this.link = undefined; + /** + * Unix group if available. + */ + this.group = undefined; + /** + * Unix user if available. + */ + this.user = undefined; + /** + * Unique ID if available. + */ + this.uniqueID = undefined; + this.name = name; + } + get isDirectory() { + return this.type === FileType.Directory; + } + get isSymbolicLink() { + return this.type === FileType.SymbolicLink; + } + get isFile() { + return this.type === FileType.File; + } + /** + * Deprecated, legacy API. Use `rawModifiedAt` instead. + * @deprecated + */ + get date() { + return this.rawModifiedAt; + } + set date(rawModifiedAt) { + this.rawModifiedAt = rawModifiedAt; + } +} +exports.FileInfo = FileInfo; +FileInfo.UnixPermission = { + Read: 4, + Write: 2, + Execute: 1 +}; + + +/***/ }), + +/***/ 9052: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_77687__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.FTPContext = exports.FTPError = void 0; +const net_1 = __nested_webpack_require_77687__(1631); +const parseControlResponse_1 = __nested_webpack_require_77687__(9948); +/** + * Describes an FTP server error response including the FTP response code. + */ +class FTPError extends Error { + constructor(res) { + super(res.message); + this.name = this.constructor.name; + this.code = res.code; + } +} +exports.FTPError = FTPError; +/** + * FTPContext holds the control and data sockets of an FTP connection and provides a + * simplified way to interact with an FTP server, handle responses, errors and timeouts. + * + * It doesn't implement or use any FTP commands. It's only a foundation to make writing an FTP + * client as easy as possible. You won't usually instantiate this, but use `Client`. + */ +class FTPContext { + /** + * Instantiate an FTP context. + * + * @param timeout - Timeout in milliseconds to apply to control and data connections. Use 0 for no timeout. + * @param encoding - Encoding to use for control connection. UTF-8 by default. Use "latin1" for older servers. + */ + constructor(timeout = 0, encoding = "utf8") { + this.timeout = timeout; + /** Debug-level logging of all socket communication. */ + this.verbose = false; + /** IP version to prefer (4: IPv4, 6: IPv6, undefined: automatic). */ + this.ipFamily = undefined; + /** Options for TLS connections. */ + this.tlsOptions = {}; + /** A multiline response might be received as multiple chunks. */ + this._partialResponse = ""; + this._encoding = encoding; + // Help Typescript understand that we do indeed set _socket in the constructor but use the setter method to do so. + this._socket = this.socket = this._newSocket(); + this._dataSocket = undefined; + } + /** + * Close the context. + */ + close() { + // Internally, closing a context is always described with an error. If there is still a task running, it will + // abort with an exception that the user closed the client during a task. If no task is running, no exception is + // thrown but all newly submitted tasks after that will abort the exception that the client has been closed. + // In addition the user will get a stack trace pointing to where exactly the client has been closed. So in any + // case use _closingError to determine whether a context is closed. This also allows us to have a single code-path + // for closing a context making the implementation easier. + const message = this._task ? "User closed client during task" : "User closed client"; + const err = new Error(message); + this.closeWithError(err); + } + /** + * Close the context with an error. + */ + closeWithError(err) { + // If this context already has been closed, don't overwrite the reason. + if (this._closingError) { + return; + } + this._closingError = err; + // Before giving the user's task a chance to react, make sure we won't be bothered with any inputs. + this._closeSocket(this._socket); + this._closeSocket(this._dataSocket); + // Give the user's task a chance to react, maybe cleanup resources. + this._passToHandler(err); + // The task might not have been rejected by the user after receiving the error. + this._stopTrackingTask(); + } + /** + * Returns true if this context has been closed or hasn't been connected yet. You can reopen it with `access`. + */ + get closed() { + return this.socket.remoteAddress === undefined || this._closingError !== undefined; + } + /** + * Reset this contex and all of its state. + */ + reset() { + this.socket = this._newSocket(); + } + /** + * Get the FTP control socket. + */ + get socket() { + return this._socket; + } + /** + * Set the socket for the control connection. This will only close the current control socket + * if the new one is not an upgrade to the current one. + */ + set socket(socket) { + // No data socket should be open in any case where the control socket is set or upgraded. + this.dataSocket = undefined; + this.tlsOptions = {}; + // This being a soft reset, remove any remaining partial response. + this._partialResponse = ""; + if (this._socket) { + // Only close the current connection if the new is not an upgrade. + const isUpgrade = socket.localPort === this._socket.localPort; + if (!isUpgrade) { + this._socket.destroy(); + } + this._removeSocketListeners(this._socket); + } + if (socket) { + // Setting a completely new control socket is in essence something like a reset. That's + // why we also close any open data connection above. We can go one step further and reset + // a possible closing error. That means that a closed FTPContext can be "reopened" by + // setting a new control socket. + this._closingError = undefined; + // Don't set a timeout yet. Timeout for control sockets is only active during a task, see handle() below. + socket.setTimeout(0); + socket.setEncoding(this._encoding); + socket.setKeepAlive(true); + socket.on("data", data => this._onControlSocketData(data)); + // Server sending a FIN packet is treated as an error. + socket.on("end", () => this.closeWithError(new Error("Server sent FIN packet unexpectedly, closing connection."))); + // Control being closed without error by server is treated as an error. + socket.on("close", hadError => { if (!hadError) + this.closeWithError(new Error("Server closed connection unexpectedly.")); }); + this._setupDefaultErrorHandlers(socket, "control socket"); + } + this._socket = socket; + } + /** + * Get the current FTP data connection if present. + */ + get dataSocket() { + return this._dataSocket; + } + /** + * Set the socket for the data connection. This will automatically close the former data socket. + */ + set dataSocket(socket) { + this._closeSocket(this._dataSocket); + if (socket) { + // Don't set a timeout yet. Timeout data socket should be activated when data transmission starts + // and timeout on control socket is deactivated. + socket.setTimeout(0); + this._setupDefaultErrorHandlers(socket, "data socket"); + } + this._dataSocket = socket; + } + /** + * Get the currently used encoding. + */ + get encoding() { + return this._encoding; + } + /** + * Set the encoding used for the control socket. + * + * See https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings for what encodings + * are supported by Node. + */ + set encoding(encoding) { + this._encoding = encoding; + if (this.socket) { + this.socket.setEncoding(encoding); + } + } + /** + * Send an FTP command without waiting for or handling the result. + */ + send(command) { + const containsPassword = command.startsWith("PASS"); + const message = containsPassword ? "> PASS ###" : `> ${command}`; + this.log(message); + this._socket.write(command + "\r\n", this.encoding); + } + /** + * Send an FTP command and handle the first response. Use this if you have a simple + * request-response situation. + */ + request(command) { + return this.handle(command, (res, task) => { + if (res instanceof Error) { + task.reject(res); + } + else { + task.resolve(res); + } + }); + } + /** + * Send an FTP command and handle any response until you resolve/reject. Use this if you expect multiple responses + * to a request. This returns a Promise that will hold whatever the response handler passed on when resolving/rejecting its task. + */ + handle(command, responseHandler) { + if (this._task) { + // The user or client instance called `handle()` while a task is still running. + const err = new Error("User launched a task while another one is still running. Forgot to use 'await' or '.then()'?"); + err.stack += `\nRunning task launched at: ${this._task.stack}`; + this.closeWithError(err); + // Don't return here, continue with returning the Promise that will then be rejected + // because the context closed already. That way, users will receive an exception where + // they called this method by mistake. + } + return new Promise((resolvePromise, rejectPromise) => { + const stack = new Error().stack || "Unknown call stack"; + const resolver = { + resolve: (...args) => { + this._stopTrackingTask(); + resolvePromise(...args); + }, + reject: err => { + this._stopTrackingTask(); + rejectPromise(err); + } + }; + this._task = { + stack, + resolver, + responseHandler + }; + if (this._closingError) { + // This client has been closed. Provide an error that describes this one as being caused + // by `_closingError`, include stack traces for both. + const err = new Error("Client is closed"); // Type 'Error' is not correctly defined, doesn't have 'code'. + err.stack += `\nClosing reason: ${this._closingError.stack}`; + err.code = this._closingError.code !== undefined ? this._closingError.code : "0"; + this._passToHandler(err); + return; + } + // Only track control socket timeout during the lifecycle of a task. This avoids timeouts on idle sockets, + // the default socket behaviour which is not expected by most users. + this.socket.setTimeout(this.timeout); + if (command) { + this.send(command); + } + }); + } + /** + * Log message if set to be verbose. + */ + log(message) { + if (this.verbose) { + // tslint:disable-next-line no-console + console.log(message); + } + } + /** + * Return true if the control socket is using TLS. This does not mean that a session + * has already been negotiated. + */ + get hasTLS() { + return "encrypted" in this._socket; + } + /** + * Removes reference to current task and handler. This won't resolve or reject the task. + * @protected + */ + _stopTrackingTask() { + // Disable timeout on control socket if there is no task active. + this.socket.setTimeout(0); + this._task = undefined; + } + /** + * Handle incoming data on the control socket. The chunk is going to be of type `string` + * because we let `socket` handle encoding with `setEncoding`. + * @protected + */ + _onControlSocketData(chunk) { + this.log(`< ${chunk}`); + // This chunk might complete an earlier partial response. + const completeResponse = this._partialResponse + chunk; + const parsed = parseControlResponse_1.parseControlResponse(completeResponse); + // Remember any incomplete remainder. + this._partialResponse = parsed.rest; + // Each response group is passed along individually. + for (const message of parsed.messages) { + const code = parseInt(message.substr(0, 3), 10); + const response = { code, message }; + const err = code >= 400 ? new FTPError(response) : undefined; + this._passToHandler(err ? err : response); + } + } + /** + * Send the current handler a response. This is usually a control socket response + * or a socket event, like an error or timeout. + * @protected + */ + _passToHandler(response) { + if (this._task) { + this._task.responseHandler(response, this._task.resolver); + } + // Errors other than FTPError always close the client. If there isn't an active task to handle the error, + // the next one submitted will receive it using `_closingError`. + // There is only one edge-case: If there is an FTPError while no task is active, the error will be dropped. + // But that means that the user sent an FTP command with no intention of handling the result. So why should the + // error be handled? Maybe log it at least? Debug logging will already do that and the client stays useable after + // FTPError. So maybe no need to do anything here. + } + /** + * Setup all error handlers for a socket. + * @protected + */ + _setupDefaultErrorHandlers(socket, identifier) { + socket.once("error", error => { + error.message += ` (${identifier})`; + this.closeWithError(error); + }); + socket.once("close", hadError => { + if (hadError) { + this.closeWithError(new Error(`Socket closed due to transmission error (${identifier})`)); + } + }); + socket.once("timeout", () => this.closeWithError(new Error(`Timeout (${identifier})`))); + } + /** + * Close a socket. + * @protected + */ + _closeSocket(socket) { + if (socket) { + socket.destroy(); + this._removeSocketListeners(socket); + } + } + /** + * Remove all default listeners for socket. + * @protected + */ + _removeSocketListeners(socket) { + socket.removeAllListeners(); + // Before Node.js 10.3.0, using `socket.removeAllListeners()` without any name did not work: https://github.com/nodejs/node/issues/20923. + socket.removeAllListeners("timeout"); + socket.removeAllListeners("data"); + socket.removeAllListeners("end"); + socket.removeAllListeners("error"); + socket.removeAllListeners("close"); + socket.removeAllListeners("connect"); + } + /** + * Provide a new socket instance. + * + * Internal use only, replaced for unit tests. + */ + _newSocket() { + return new net_1.Socket(); + } +} +exports.FTPContext = FTPContext; + + +/***/ }), + +/***/ 7170: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.ProgressTracker = void 0; +/** + * Tracks progress of one socket data transfer at a time. + */ +class ProgressTracker { + constructor() { + this.bytesOverall = 0; + this.intervalMs = 500; + this.onStop = noop; + this.onHandle = noop; + } + /** + * Register a new handler for progress info. Use `undefined` to disable reporting. + */ + reportTo(onHandle = noop) { + this.onHandle = onHandle; + } + /** + * Start tracking transfer progress of a socket. + * + * @param socket The socket to observe. + * @param name A name associated with this progress tracking, e.g. a filename. + * @param type The type of the transfer, typically "upload" or "download". + */ + start(socket, name, type) { + let lastBytes = 0; + this.onStop = poll(this.intervalMs, () => { + const bytes = socket.bytesRead + socket.bytesWritten; + this.bytesOverall += bytes - lastBytes; + lastBytes = bytes; + this.onHandle({ + name, + type, + bytes, + bytesOverall: this.bytesOverall + }); + }); + } + /** + * Stop tracking transfer progress. + */ + stop() { + this.onStop(false); + } + /** + * Call the progress handler one more time, then stop tracking. + */ + updateAndStop() { + this.onStop(true); + } +} +exports.ProgressTracker = ProgressTracker; +/** + * Starts calling a callback function at a regular interval. The first call will go out + * immediately. The function returns a function to stop the polling. + */ +function poll(intervalMs, updateFunc) { + const id = setInterval(updateFunc, intervalMs); + const stopFunc = (stopWithUpdate) => { + clearInterval(id); + if (stopWithUpdate) { + updateFunc(); + } + // Prevent repeated calls to stop calling handler. + updateFunc = noop; + }; + updateFunc(); + return stopFunc; +} +function noop() { } + + +/***/ }), + +/***/ 4677: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); + + +/***/ }), + +/***/ 8184: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_94586__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.StringWriter = void 0; +const stream_1 = __nested_webpack_require_94586__(2413); +class StringWriter extends stream_1.Writable { + constructor() { + super(...arguments); + this.buf = Buffer.alloc(0); + } + _write(chunk, _, callback) { + if (chunk instanceof Buffer) { + this.buf = Buffer.concat([this.buf, chunk]); + callback(null); + } + else { + callback(new Error("StringWriter expects chunks of type 'Buffer'.")); + } + } + getText(encoding) { + return this.buf.toString(encoding); + } +} +exports.StringWriter = StringWriter; + + +/***/ }), + +/***/ 7957: +/***/ (function(__unused_webpack_module, exports, __nested_webpack_require_95374__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +/** + * Public API + */ +__exportStar(__nested_webpack_require_95374__(8337), exports); +__exportStar(__nested_webpack_require_95374__(9052), exports); +__exportStar(__nested_webpack_require_95374__(202), exports); +__exportStar(__nested_webpack_require_95374__(2993), exports); +__exportStar(__nested_webpack_require_95374__(4677), exports); +var transfer_1 = __nested_webpack_require_95374__(5803); +Object.defineProperty(exports, "enterPassiveModeIPv4", ({ enumerable: true, get: function () { return transfer_1.enterPassiveModeIPv4; } })); +Object.defineProperty(exports, "enterPassiveModeIPv6", ({ enumerable: true, get: function () { return transfer_1.enterPassiveModeIPv6; } })); + + +/***/ }), + +/***/ 6288: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_96633__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.ipIsPrivateV4Address = exports.upgradeSocket = exports.describeAddress = exports.describeTLS = void 0; +const tls_1 = __nested_webpack_require_96633__(4016); +/** + * Returns a string describing the encryption on a given socket instance. + */ +function describeTLS(socket) { + if (socket instanceof tls_1.TLSSocket) { + const protocol = socket.getProtocol(); + return protocol ? protocol : "Server socket or disconnected client socket"; + } + return "No encryption"; +} +exports.describeTLS = describeTLS; +/** + * Returns a string describing the remote address of a socket. + */ +function describeAddress(socket) { + if (socket.remoteFamily === "IPv6") { + return `[${socket.remoteAddress}]:${socket.remotePort}`; + } + return `${socket.remoteAddress}:${socket.remotePort}`; +} +exports.describeAddress = describeAddress; +/** + * Upgrade a socket connection with TLS. + */ +function upgradeSocket(socket, options) { + return new Promise((resolve, reject) => { + const tlsOptions = Object.assign({}, options, { + socket + }); + const tlsSocket = tls_1.connect(tlsOptions, () => { + const expectCertificate = tlsOptions.rejectUnauthorized !== false; + if (expectCertificate && !tlsSocket.authorized) { + reject(tlsSocket.authorizationError); + } + else { + // Remove error listener added below. + tlsSocket.removeAllListeners("error"); + resolve(tlsSocket); + } + }).once("error", error => { + reject(error); + }); + }); +} +exports.upgradeSocket = upgradeSocket; +/** + * Returns true if an IP is a private address according to https://tools.ietf.org/html/rfc1918#section-3. + * This will handle IPv4-mapped IPv6 addresses correctly but return false for all other IPv6 addresses. + * + * @param ip The IP as a string, e.g. "192.168.0.1" + */ +function ipIsPrivateV4Address(ip = "") { + // Handle IPv4-mapped IPv6 addresses like ::ffff:192.168.0.1 + if (ip.startsWith("::ffff:")) { + ip = ip.substr(7); // Strip ::ffff: prefix + } + const octets = ip.split(".").map(o => parseInt(o, 10)); + return octets[0] === 10 // 10.0.0.0 - 10.255.255.255 + || (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) // 172.16.0.0 - 172.31.255.255 + || (octets[0] === 192 && octets[1] === 168); // 192.168.0.0 - 192.168.255.255 +} +exports.ipIsPrivateV4Address = ipIsPrivateV4Address; + + +/***/ }), + +/***/ 9948: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.positiveIntermediate = exports.positiveCompletion = exports.isMultiline = exports.isSingleLine = exports.parseControlResponse = void 0; +const LF = "\n"; +/** + * Parse an FTP control response as a collection of messages. A message is a complete + * single- or multiline response. A response can also contain multiple multiline responses + * that will each be represented by a message. A response can also be incomplete + * and be completed on the next incoming data chunk for which case this function also + * describes a `rest`. This function converts all CRLF to LF. + */ +function parseControlResponse(text) { + const lines = text.split(/\r?\n/).filter(isNotBlank); + const messages = []; + let startAt = 0; + let tokenRegex; + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + // No group has been opened. + if (!tokenRegex) { + if (isMultiline(line)) { + // Open a group by setting an expected token. + const token = line.substr(0, 3); + tokenRegex = new RegExp(`^${token}(?:$| )`); + startAt = i; + } + else if (isSingleLine(line)) { + // Single lines can be grouped immediately. + messages.push(line); + } + } + // Group has been opened, expect closing token. + else if (tokenRegex.test(line)) { + tokenRegex = undefined; + messages.push(lines.slice(startAt, i + 1).join(LF)); + } + } + // The last group might not have been closed, report it as a rest. + const rest = tokenRegex ? lines.slice(startAt).join(LF) + LF : ""; + return { messages, rest }; +} +exports.parseControlResponse = parseControlResponse; +function isSingleLine(line) { + return /^\d\d\d(?:$| )/.test(line); +} +exports.isSingleLine = isSingleLine; +function isMultiline(line) { + return /^\d\d\d-/.test(line); +} +exports.isMultiline = isMultiline; +/** + * Return true if an FTP return code describes a positive completion. + */ +function positiveCompletion(code) { + return code >= 200 && code < 300; +} +exports.positiveCompletion = positiveCompletion; +/** + * Return true if an FTP return code describes a positive intermediate response. + */ +function positiveIntermediate(code) { + return code >= 300 && code < 400; +} +exports.positiveIntermediate = positiveIntermediate; +function isNotBlank(str) { + return str !== ""; +} + + +/***/ }), + +/***/ 2993: +/***/ (function(__unused_webpack_module, exports, __nested_webpack_require_101849__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.parseList = void 0; +const dosParser = __importStar(__nested_webpack_require_101849__(6199)); +const unixParser = __importStar(__nested_webpack_require_101849__(2622)); +const mlsdParser = __importStar(__nested_webpack_require_101849__(8157)); +/** + * Available directory listing parsers. These are candidates that will be tested + * in the order presented. The first candidate will be used to parse the whole list. + */ +const availableParsers = [ + dosParser, + unixParser, + mlsdParser // Keep MLSD last, may accept filename only +]; +function firstCompatibleParser(line, parsers) { + return parsers.find(parser => parser.testLine(line) === true); +} +function stringIsNotBlank(str) { + return str.trim() !== ""; +} +const REGEX_NEWLINE = /\r?\n/; +/** + * Parse raw directory listing. + */ +function parseList(rawList) { + const lines = rawList + .split(REGEX_NEWLINE) + .filter(stringIsNotBlank); + if (lines.length === 0) { + return []; + } + const testLine = lines[lines.length - 1]; + const parser = firstCompatibleParser(testLine, availableParsers); + if (!parser) { + throw new Error("This library only supports MLSD, Unix- or DOS-style directory listing. Your FTP server seems to be using another format. You can see the transmitted listing when setting `client.ftp.verbose = true`. You can then provide a custom parser to `client.parseList`, see the documentation for details."); + } + const files = lines + .map(parser.parseLine) + .filter((info) => info !== undefined); + return parser.transformList(files); +} +exports.parseList = parseList; + + +/***/ }), + +/***/ 6199: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_104437__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); exports.transformList = exports.parseLine = exports.testLine = void 0; -const FileInfo_1 = __nested_webpack_require_91612__(202); +const FileInfo_1 = __nested_webpack_require_104437__(202); +/** + * This parser is based on the FTP client library source code in Apache Commons Net provided + * under the Apache 2.0 license. It has been simplified and rewritten to better fit the Javascript language. + * + * https://github.com/apache/commons-net/blob/master/src/main/java/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java + */ +const RE_LINE = new RegExp("(\\S+)\\s+(\\S+)\\s+" // MM-dd-yy whitespace hh:mma|kk:mm swallow trailing spaces + + "(?:()|([0-9]+))\\s+" // or ddddd swallow trailing spaces + + "(\\S.*)" // First non-space followed by rest of line (name) +); +/** + * Returns true if a given line might be a DOS-style listing. + * + * - Example: `12-05-96 05:03PM myDir` + */ +function testLine(line) { + return /^\d{2}/.test(line) && RE_LINE.test(line); +} +exports.testLine = testLine; +/** + * Parse a single line of a DOS-style directory listing. + */ +function parseLine(line) { + const groups = line.match(RE_LINE); + if (groups === null) { + return undefined; + } + const name = groups[5]; + if (name === "." || name === "..") { // Ignore parent directory links + return undefined; + } + const file = new FileInfo_1.FileInfo(name); + const fileType = groups[3]; + if (fileType === "") { + file.type = FileInfo_1.FileType.Directory; + file.size = 0; + } + else { + file.type = FileInfo_1.FileType.File; + file.size = parseInt(groups[4], 10); + } + file.rawModifiedAt = groups[1] + " " + groups[2]; + return file; +} +exports.parseLine = parseLine; +function transformList(files) { + return files; +} +exports.transformList = transformList; + + +/***/ }), + +/***/ 8157: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_106383__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.parseMLSxDate = exports.transformList = exports.parseLine = exports.testLine = void 0; +const FileInfo_1 = __nested_webpack_require_106383__(202); +function parseSize(value, info) { + info.size = parseInt(value, 10); +} +/** + * Parsers for MLSD facts. + */ +const factHandlersByName = { + "size": parseSize, + "sizd": parseSize, + "unique": (value, info) => { + info.uniqueID = value; + }, + "modify": (value, info) => { + info.modifiedAt = parseMLSxDate(value); + info.rawModifiedAt = info.modifiedAt.toISOString(); + }, + "type": (value, info) => { + // There seems to be confusion on how to handle symbolic links for Unix. RFC 3659 doesn't describe + // this but mentions some examples using the syntax `type=OS.unix=slink:`. But according to + // an entry in the Errata (https://www.rfc-editor.org/errata/eid1500) this syntax can't be valid. + // Instead it proposes to use `type=OS.unix=symlink` and to then list the actual target of the + // symbolic link as another entry in the directory listing. The unique identifiers can then be used + // to derive the connection between link(s) and target. We'll have to handle both cases as there + // are differing opinions on how to deal with this. Here are some links on this topic: + // - ProFTPD source: https://github.com/proftpd/proftpd/blob/56e6dfa598cbd4ef5c6cba439bcbcd53a63e3b21/modules/mod_facts.c#L531 + // - ProFTPD bug: http://bugs.proftpd.org/show_bug.cgi?id=3318 + // - ProFTPD statement: http://www.proftpd.org/docs/modules/mod_facts.html + // – FileZilla bug: https://trac.filezilla-project.org/ticket/9310 + if (value.startsWith("OS.unix=slink")) { + info.type = FileInfo_1.FileType.SymbolicLink; + info.link = value.substr(value.indexOf(":") + 1); + return 1 /* Continue */; + } + switch (value) { + case "file": + info.type = FileInfo_1.FileType.File; + break; + case "dir": + info.type = FileInfo_1.FileType.Directory; + break; + case "OS.unix=symlink": + info.type = FileInfo_1.FileType.SymbolicLink; + // The target of the symbolic link might be defined in another line in the directory listing. + // We'll handle this in `transformList()` below. + break; + case "cdir": // Current directory being listed + case "pdir": // Parent directory + return 2 /* IgnoreFile */; // Don't include these entries in the listing + default: + info.type = FileInfo_1.FileType.Unknown; + } + return 1 /* Continue */; + }, + "unix.mode": (value, info) => { + const digits = value.substr(-3); + info.permissions = { + user: parseInt(digits[0], 10), + group: parseInt(digits[1], 10), + world: parseInt(digits[2], 10) + }; + }, + "unix.ownername": (value, info) => { + info.user = value; + }, + "unix.owner": (value, info) => { + if (info.user === undefined) + info.user = value; + }, + get "unix.uid"() { + return this["unix.owner"]; + }, + "unix.groupname": (value, info) => { + info.group = value; + }, + "unix.group": (value, info) => { + if (info.group === undefined) + info.group = value; + }, + get "unix.gid"() { + return this["unix.group"]; + } + // Regarding the fact "perm": + // We don't handle permission information stored in "perm" because its information is conceptually + // different from what users of FTP clients usually associate with "permissions". Those that have + // some expectations (and probably want to edit them with a SITE command) often unknowingly expect + // the Unix permission system. The information passed by "perm" describes what FTP commands can be + // executed with a file/directory. But even this can be either incomplete or just meant as a "guide" + // as the spec mentions. From https://tools.ietf.org/html/rfc3659#section-7.5.5: "The permissions are + // described here as they apply to FTP commands. They may not map easily into particular permissions + // available on the server's operating system." The parser by Apache Commons tries to translate these + // to Unix permissions – this is misleading users and might not even be correct. +}; +/** + * Split a string once at the first position of a delimiter. For example + * `splitStringOnce("a b c d", " ")` returns `["a", "b c d"]`. + */ +function splitStringOnce(str, delimiter) { + const pos = str.indexOf(delimiter); + const a = str.substr(0, pos); + const b = str.substr(pos + delimiter.length); + return [a, b]; +} +/** + * Returns true if a given line might be part of an MLSD listing. + * + * - Example 1: `size=15227;type=dir;perm=el;modify=20190419065730; test one` + * - Example 2: ` file name` (leading space) + */ +function testLine(line) { + return /^\S+=\S+;/.test(line) || line.startsWith(" "); +} +exports.testLine = testLine; +/** + * Parse single line as MLSD listing, see specification at https://tools.ietf.org/html/rfc3659#section-7. + */ +function parseLine(line) { + const [packedFacts, name] = splitStringOnce(line, " "); + if (name === "" || name === "." || name === "..") { + return undefined; + } + const info = new FileInfo_1.FileInfo(name); + const facts = packedFacts.split(";"); + for (const fact of facts) { + const [factName, factValue] = splitStringOnce(fact, "="); + if (!factValue) { + continue; + } + const factHandler = factHandlersByName[factName.toLowerCase()]; + if (!factHandler) { + continue; + } + const result = factHandler(factValue, info); + if (result === 2 /* IgnoreFile */) { + return undefined; + } + } + return info; +} +exports.parseLine = parseLine; +function transformList(files) { + // Create a map of all files that are not symbolic links by their unique ID + const nonLinksByID = new Map(); + for (const file of files) { + if (!file.isSymbolicLink && file.uniqueID !== undefined) { + nonLinksByID.set(file.uniqueID, file); + } + } + const resolvedFiles = []; + for (const file of files) { + // Try to associate unresolved symbolic links with a target file/directory. + if (file.isSymbolicLink && file.uniqueID !== undefined && file.link === undefined) { + const target = nonLinksByID.get(file.uniqueID); + if (target !== undefined) { + file.link = target.name; + } + } + // The target of a symbolic link is listed as an entry in the directory listing but might + // have a path pointing outside of this directory. In that case we don't want this entry + // to be part of the listing. We generally don't want these kind of entries at all. + const isPartOfDirectory = !file.name.includes("/"); + if (isPartOfDirectory) { + resolvedFiles.push(file); + } + } + return resolvedFiles; +} +exports.transformList = transformList; +/** + * Parse date as specified in https://tools.ietf.org/html/rfc3659#section-2.3. + * + * Message contains response code and modified time in the format: YYYYMMDDHHMMSS[.sss] + * For example `19991005213102` or `19980615100045.014`. + */ +function parseMLSxDate(fact) { + return new Date(Date.UTC(+fact.slice(0, 4), // Year + +fact.slice(4, 6) - 1, // Month + +fact.slice(6, 8), // Date + +fact.slice(8, 10), // Hours + +fact.slice(10, 12), // Minutes + +fact.slice(12, 14), // Seconds + +fact.slice(15, 18) // Milliseconds + )); +} +exports.parseMLSxDate = parseMLSxDate; + + +/***/ }), + +/***/ 2622: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_114318__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.transformList = exports.parseLine = exports.testLine = void 0; +const FileInfo_1 = __nested_webpack_require_114318__(202); const JA_MONTH = "\u6708"; const JA_DAY = "\u65e5"; const JA_YEAR = "\u5e74"; @@ -2760,1684 +3728,16 @@ function parseMode(r, w, x) { /***/ }), -/***/ 405: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_97485__) { +/***/ 5803: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_120198__) => { "use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.filePathFilter = void 0; -const create_filter_1 = __nested_webpack_require_97485__(176); -function filePathFilter(...args) { - return create_filter_1.createFilter({}, ...args); -} -exports.filePathFilter = filePathFilter; -//# sourceMappingURL=file-path-filter.js.map - -/***/ }), - -/***/ 410: -/***/ (function(module, exports, __nested_webpack_require_97903__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.filePathFilter = void 0; -const file_path_filter_1 = __nested_webpack_require_97903__(405); -Object.defineProperty(exports, "filePathFilter", { enumerable: true, get: function () { return file_path_filter_1.filePathFilter; } }); -__exportStar(__nested_webpack_require_97903__(225), exports); -var create_filter_1 = __nested_webpack_require_97903__(176); -Object.defineProperty(exports, "createFilter", { enumerable: true, get: function () { return create_filter_1.createFilter; } }); -// Export `filePathFilter` as a named export and the default export -exports.default = file_path_filter_1.filePathFilter; -// CommonJS default export hack -/* eslint-env commonjs */ -if ( true && typeof module.exports === "object") { - module.exports = Object.assign(module.exports.default, module.exports); -} -//# sourceMappingURL=index.js.map - -/***/ }), - -/***/ 413: -/***/ (function(module) { - -module.exports = __webpack_require__(413); - -/***/ }), - -/***/ 417: -/***/ (function(module) { - -module.exports = __webpack_require__(417); - -/***/ }), - -/***/ 445: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_99520__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.stat = void 0; -const call_1 = __nested_webpack_require_99520__(188); -/** - * Retrieves the `Stats` for the given path. If the path is a symbolic link, - * then the Stats of the symlink's target are returned instead. If the symlink is broken, - * then the Stats of the symlink itself are returned. - * - * @param fs - Synchronous or Asynchronouse facade for the "fs" module - * @param path - The path to return stats for - * - * @internal - */ -function stat(fs, path, callback) { - let isSymLink = false; - call_1.safeCall(fs.lstat, path, (err, lstats) => { - if (err) { - // fs.lstat threw an eror - return callback(err, undefined); - } - try { - isSymLink = lstats.isSymbolicLink(); - } - catch (err2) { - // lstats.isSymbolicLink() threw an error - // (probably because fs.lstat returned an invalid result) - return callback(err2, undefined); - } - if (isSymLink) { - // Try to resolve the symlink - symlinkStat(fs, path, lstats, callback); - } - else { - // It's not a symlink, so return the stats as-is - callback(null, lstats); - } - }); -} -exports.stat = stat; -/** - * Retrieves the `Stats` for the target of the given symlink. - * If the symlink is broken, then the Stats of the symlink itself are returned. - * - * @param fs - Synchronous or Asynchronouse facade for the "fs" module - * @param path - The path of the symlink to return stats for - * @param lstats - The stats of the symlink - */ -function symlinkStat(fs, path, lstats, callback) { - call_1.safeCall(fs.stat, path, (err, stats) => { - if (err) { - // The symlink is broken, so return the stats for the link itself - return callback(null, lstats); - } - try { - // Return the stats for the resolved symlink target, - // and override the `isSymbolicLink` method to indicate that it's a symlink - stats.isSymbolicLink = () => true; - } - catch (err2) { - // Setting stats.isSymbolicLink threw an error - // (probably because fs.stat returned an invalid result) - return callback(err2, undefined); - } - callback(null, stats); - }); -} -//# sourceMappingURL=stat.js.map - -/***/ }), - -/***/ 448: -/***/ (function(__unusedmodule, exports) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.syncForEach = void 0; -/** - * A facade that allows `Array.forEach()` to be called as though it were asynchronous. - * - * @param array - The array to iterate over - * @param iterator - The function to call for each item in the array - * @param done - The function to call when all iterators have completed - * - * @internal - */ -function syncForEach(array, iterator, done) { - if (!Array.isArray(array)) { - throw new TypeError(`${array} is not an array`); - } - for (let item of array) { - iterator(item, () => { - // Note: No error-handling here because this is currently only ever called - // by DirectoryReader, which never passes an `error` parameter to the callback. - // Instead, DirectoryReader emits an "error" event if an error occurs. - }); - } - done(); -} -exports.syncForEach = syncForEach; -//# sourceMappingURL=for-each.js.map - -/***/ }), - -/***/ 504: -/***/ (function(__unusedmodule, exports) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.asyncForEach = void 0; -/** - * Simultaneously processes all items in the given array. - * - * @param array - The array to iterate over - * @param iterator - The function to call for each item in the array - * @param done - The function to call when all iterators have completed - * - * @internal - */ -function asyncForEach(array, iterator, done) { - if (!Array.isArray(array)) { - throw new TypeError(`${array} is not an array`); - } - if (array.length === 0) { - // NOTE: Normally a bad idea to mix sync and async, but it's safe here because - // of the way that this method is currently used by DirectoryReader. - done(); - return; - } - // Simultaneously process all items in the array. - let pending = array.length; - for (let item of array) { - iterator(item, callback); - } - function callback() { - if (--pending === 0) { - done(); - } - } -} -exports.asyncForEach = asyncForEach; -//# sourceMappingURL=for-each.js.map - -/***/ }), - -/***/ 521: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_104182__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.readdirStream = void 0; -const fs = __nested_webpack_require_104182__(747); -const for_each_1 = __nested_webpack_require_104182__(504); -const directory_reader_1 = __nested_webpack_require_104182__(918); -const streamFacade = { fs, forEach: for_each_1.asyncForEach }; -function readdirStream(dir, options) { - let reader = new directory_reader_1.DirectoryReader(dir, options, streamFacade, true); - return reader.stream; -} -exports.readdirStream = readdirStream; -//# sourceMappingURL=index.js.map - -/***/ }), - -/***/ 546: -/***/ (function(module) { - -"use strict"; - - -const arrify = value => { - if (value === null || value === undefined) { - return []; - } - - if (Array.isArray(value)) { - return value; - } - - if (typeof value === 'string') { - return [value]; - } - - if (typeof value[Symbol.iterator] === 'function') { - return [...value]; - } - - return [value]; -}; - -module.exports = arrify; - - -/***/ }), - -/***/ 553: -/***/ (function(__unusedmodule, exports) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.pending = void 0; -/** - * Returns a `Promise` and the functions to resolve or reject it. - * @internal - */ -function pending() { - let resolve, reject; - let promise = new Promise((res, rej) => { - resolve = res; - reject = rej; - }); - return { - promise, - resolve(result) { - // eslint-disable-next-line @typescript-eslint/no-floating-promises - Promise.resolve(result).then(resolve); - }, - reject(reason) { - Promise.reject(reason).catch(reject); - } - }; -} -exports.pending = pending; -//# sourceMappingURL=pending.js.map - -/***/ }), - -/***/ 554: -/***/ (function(module) { - -"use strict"; - - -const arrayDiffer = (array, ...values) => { - const rest = new Set([].concat(...values)); - return array.filter(element => !rest.has(element)); -}; - -module.exports = arrayDiffer; - - -/***/ }), - -/***/ 561: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_106195__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.normalize = void 0; -const globToRegExp = __nested_webpack_require_106195__(117); -const path = __nested_webpack_require_106195__(622); -const util_1 = __nested_webpack_require_106195__(735); -/** - * Normalizes the user-provided filter criteria. The normalized form is a `Filters` object - * whose `include` and `exclude` properties are both `FilterFunction` arrays. - */ -function normalize(criteria, opts) { - let filters = { - include: [], - exclude: [], - }; - let options = normalizeOptions(opts); - // Convert each criterion to a FilterFunction - let tuples = normalizeCriteria(criteria, options); - // Populate the `include` and `exclude` arrays - for (let [filter, filterFunction] of tuples) { - filters[filter].push(filterFunction); - } - return filters; -} -exports.normalize = normalize; -/** - * Fills-in defaults for any options that weren't specified by the caller. - */ -function normalizeOptions(options) { - return { - // TODO: Remove the "getPath" fallback in the next minor release - map: options.map || options.getPath || String, - sep: options.sep || path.sep, - }; -} -/** - * Creates a `FilterFunction` for each given criterion. - */ -function normalizeCriteria(criteria, options, filter) { - let tuples = []; - if (Array.isArray(criteria)) { - for (let criterion of criteria) { - tuples.push(...normalizeCriteria(criterion, options, filter)); - } - } - else if (util_1.isPathFilter(criteria)) { - for (let filterFunction of criteria[util_1._filters].include) { - tuples.push(["include", filterFunction]); - } - for (let filterFunction of criteria[util_1._filters].exclude) { - tuples.push(["exclude", filterFunction]); - } - } - else if (util_1.isFilterCriterion(criteria)) { - tuples.push(normalizeCriterion(criteria, options, filter)); - } - else if (criteria && typeof criteria === "object" && !filter) { - if (criteria.include !== undefined) { - tuples.push(...normalizeCriteria(criteria.include, options, "include")); - } - if (criteria.exclude !== undefined) { - tuples.push(...normalizeCriteria(criteria.exclude, options, "exclude")); - } - } - else { - throw new Error(`Invalid filter criteria: ${criteria}`); - } - return tuples; -} -/** - * Creates a `FilterFunction` for the given criterion. - * - * @param criteria - One or more filter critiera - * @param options - Options for how the `FilterFunction` should behave - * @param filter - The type of filter. Defaults to `include`, except for glob patterns that start with "!" - */ -function normalizeCriterion(criterion, options, filter) { - const globOptions = { extended: true, globstar: true }; - let type = typeof criterion; - let filterFunction; - if (type === "function") { - filterFunction = criterion; - } - else if (type === "boolean") { - let bool = criterion; - filterFunction = function booleanFilter() { - return bool; - }; - } - else if (type === "string") { - let glob = criterion; - let invert = false; - if (glob.startsWith("!")) { - glob = glob.substr(1); - invert = Boolean(filter); - filter = filter || "exclude"; - } - let pattern = globToRegExp(glob, globOptions); - filterFunction = createGlobFilter(pattern, options, invert); - } - else if (criterion instanceof RegExp) { - let pattern = criterion; - let { map } = options; - filterFunction = function regExpFilter(...args) { - let filePath = map(...args); - return pattern.test(filePath); - }; - } - else { - throw new Error(`Invalid filter criteria: ${criterion}`); - } - return [filter || "include", filterFunction]; -} -/** - * Creates a `FilterFunction` for filtering based on glob patterns - */ -function createGlobFilter(pattern, options, invert) { - let { map, sep } = options; - return function globFilter(...args) { - let filePath = map(...args); - if (sep !== "/") { - // Glob patterns always expect forward slashes, even on Windows - filePath = filePath.replace(new RegExp("\\" + sep, "g"), "/"); - } - let match = pattern.test(filePath); - return invert ? !match : match; - }; -} -//# sourceMappingURL=normalize.js.map - -/***/ }), - -/***/ 600: -/***/ (function(module) { - -"use strict"; - - -module.exports = (...arguments_) => { - return [...new Set([].concat(...arguments_))]; -}; - - -/***/ }), - -/***/ 611: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_110878__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -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 __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.deploy = exports.excludeDefaults = void 0; -var ftp = __importStar(__nested_webpack_require_110878__(957)); -var readdir_enhanced_1 = __importDefault(__nested_webpack_require_110878__(811)); -var crypto_1 = __importDefault(__nested_webpack_require_110878__(417)); -var fs_1 = __importDefault(__nested_webpack_require_110878__(747)); -var multiMatch_1 = __importDefault(__nested_webpack_require_110878__(865)); -var stream_1 = __nested_webpack_require_110878__(413); -var types_1 = __nested_webpack_require_110878__(77); -var HashDiff_1 = __nested_webpack_require_110878__(262); -var utilities_1 = __nested_webpack_require_110878__(739); -var pretty_bytes_1 = __importDefault(__nested_webpack_require_110878__(168)); -var errorHandling_1 = __nested_webpack_require_110878__(78); -exports.excludeDefaults = [".git*", ".git*/**", "node_modules/**", "node_modules/**/*"]; -function fileHash(filename, algorithm) { - return __awaiter(this, void 0, void 0, function () { - return __generator(this, function (_a) { - return [2 /*return*/, new Promise(function (resolve, reject) { - // Algorithm depends on availability of OpenSSL on platform - // Another algorithms: "sha1", "md5", "sha256", "sha512" ... - var shasum = crypto_1.default.createHash(algorithm); - try { - var s = fs_1.default.createReadStream(filename); - s.on("data", function (data) { - shasum.update(data); - }); - // making digest - s.on("end", function () { - var hash = shasum.digest("hex"); - return resolve(hash); - }); - } - catch (error) { - return reject("calc fail"); - } - })]; - }); - }); -} -// Excludes takes precedence over includes -function includeExcludeFilter(stat, args) { - // match exclude, return immediatley - if (args.exclude !== null) { - var exclude = multiMatch_1.default(stat.path, args.exclude, { matchBase: true, dot: true }); - if (exclude.length > 0) { - return false; - } - } - if (args.include !== null) { - // matches include - return immediatley - var include = multiMatch_1.default(stat.path, args.include, { matchBase: true, dot: true }); - if (include.length > 0) { - return true; - } - } - return true; -} -function getLocalFiles(args) { - return __awaiter(this, void 0, void 0, function () { - var files, records, _i, files_1, stat, _a, _b, _c; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: return [4 /*yield*/, readdir_enhanced_1.default.async(args["local-dir"], { deep: true, stats: true, sep: "/", filter: function (stat) { return includeExcludeFilter(stat, args); } })]; - case 1: - files = _d.sent(); - records = []; - _i = 0, files_1 = files; - _d.label = 2; - case 2: - if (!(_i < files_1.length)) return [3 /*break*/, 6]; - stat = files_1[_i]; - if (stat.isDirectory()) { - records.push({ - type: "folder", - name: stat.path, - size: undefined - }); - return [3 /*break*/, 5]; - } - if (!stat.isFile()) return [3 /*break*/, 4]; - _b = (_a = records).push; - _c = { - type: "file", - name: stat.path, - size: stat.size - }; - return [4 /*yield*/, fileHash(stat.path, "sha256")]; - case 3: - _b.apply(_a, [(_c.hash = _d.sent(), - _c)]); - return [3 /*break*/, 5]; - case 4: - if (stat.isSymbolicLink()) { - console.warn("Currently unable to handle symbolic links"); - } - _d.label = 5; - case 5: - _i++; - return [3 /*break*/, 2]; - case 6: return [2 /*return*/, { - description: types_1.syncFileDescription, - version: types_1.currentVersion, - generatedTime: new Date().getTime(), - data: records - }]; - } - }); - }); -} -function downloadFileList(client, path) { - return __awaiter(this, void 0, void 0, function () { - var _this = this; - return __generator(this, function (_a) { - return [2 /*return*/, new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () { - var downloadStream, chunks; - return __generator(this, function (_a) { - downloadStream = new stream_1.Stream.Writable(); - chunks = []; - downloadStream._write = function (chunk, encoding, next) { - chunks.push(chunk); - next(); - }; - downloadStream.on("error", reject); - downloadStream.on("finish", function () { - var file = Buffer.concat(chunks).toString("utf8"); - try { - resolve(JSON.parse(file)); - } - catch (e) { - reject(e); - } - }); - client.downloadTo(downloadStream, path).catch(function (reason) { - reject("Can't open due to: \"" + reason + "\""); - }); - return [2 /*return*/]; - }); - }); })]; - }); - }); -} -/** - * Converts a file path (ex: "folder/otherfolder/file.txt") to an array of folder and a file path - * @param fullPath - */ -function getFileBreadcrumbs(fullPath) { - var _a; - // todo see if this regex will work for nonstandard folder names - // todo what happens if the path is relative to the root dir? (starts with /) - var pathSplit = fullPath.split("/"); - var file = (_a = pathSplit === null || pathSplit === void 0 ? void 0 : pathSplit.pop()) !== null && _a !== void 0 ? _a : ""; // get last item - var folders = pathSplit.filter(function (folderName) { return folderName != ""; }); - return { - folders: folders.length === 0 ? null : folders, - file: file === "" ? null : file - }; -} -/** - * Navigates up {dirCount} number of directories from the current working dir - */ -function upDir(client, dirCount) { - return __awaiter(this, void 0, void 0, function () { - var i; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - if (typeof dirCount !== "number") { - return [2 /*return*/]; - } - i = 0; - _a.label = 1; - case 1: - if (!(i < dirCount)) return [3 /*break*/, 4]; - return [4 /*yield*/, client.cdup()]; - case 2: - _a.sent(); - _a.label = 3; - case 3: - i++; - return [3 /*break*/, 1]; - case 4: return [2 /*return*/]; - } - }); - }); -} -/** - * - * @param client ftp client - * @param file file can include folder(s) - * Note working dir is modified and NOT reset after upload - * For now we are going to reset it - but this will be removed for performance - */ -function uploadFile(client, filePath, logger, type) { - var _a; - if (type === void 0) { type = "upload"; } - return __awaiter(this, void 0, void 0, function () { - var typePresent, typePast, path; - return __generator(this, function (_b) { - switch (_b.label) { - case 0: - typePresent = type === "upload" ? "uploading" : "replacing"; - typePast = type === "upload" ? "uploaded" : "replaced"; - logger.all(typePresent + " \"" + filePath + "\""); - path = getFileBreadcrumbs(filePath); - if (!(path.folders === null)) return [3 /*break*/, 1]; - logger.debug(" no need to change dir"); - return [3 /*break*/, 3]; - case 1: - logger.debug(" changing dir to " + path.folders.join("/")); - return [4 /*yield*/, client.ensureDir(path.folders.join("/"))]; - case 2: - _b.sent(); - logger.debug(" dir changed"); - _b.label = 3; - case 3: - if (!(path.file !== null)) return [3 /*break*/, 5]; - logger.debug(" " + type + " started"); - return [4 /*yield*/, client.uploadFrom(filePath, path.file)]; - case 4: - _b.sent(); - logger.debug(" file " + typePast); - _b.label = 5; - case 5: - // navigate back to the root folder - return [4 /*yield*/, upDir(client, (_a = path.folders) === null || _a === void 0 ? void 0 : _a.length)]; - case 6: - // navigate back to the root folder - _b.sent(); - logger.debug(" completed"); - return [2 /*return*/]; - } - }); - }); -} -function createFolder(client, folderPath, logger) { - var _a; - return __awaiter(this, void 0, void 0, function () { - var path; - return __generator(this, function (_b) { - switch (_b.label) { - case 0: - logger.all("creating folder \"" + (folderPath + "/") + "\""); - path = getFileBreadcrumbs(folderPath + "/"); - if (!(path.folders === null)) return [3 /*break*/, 1]; - logger.debug(" no need to change dir"); - return [3 /*break*/, 3]; - case 1: - logger.debug(" creating folder " + path.folders.join("/")); - return [4 /*yield*/, client.ensureDir(path.folders.join("/"))]; - case 2: - _b.sent(); - _b.label = 3; - case 3: - // navigate back to the root folder - return [4 /*yield*/, upDir(client, (_a = path.folders) === null || _a === void 0 ? void 0 : _a.length)]; - case 4: - // navigate back to the root folder - _b.sent(); - logger.debug(" completed"); - return [2 /*return*/]; - } - }); - }); -} -function removeFolder(client, folderPath, logger) { - var _a; - return __awaiter(this, void 0, void 0, function () { - var path, e_1, error; - return __generator(this, function (_b) { - switch (_b.label) { - case 0: - logger.all("removing folder \"" + (folderPath + "/") + "\""); - path = getFileBreadcrumbs(folderPath + "/"); - if (!(path.folders === null)) return [3 /*break*/, 1]; - logger.debug(" no need to change dir"); - return [3 /*break*/, 4]; - case 1: - _b.trys.push([1, 3, , 4]); - logger.debug(" removing folder \"" + (path.folders.join("/") + "/") + "\""); - return [4 /*yield*/, client.removeDir(path.folders.join("/") + "/")]; - case 2: - _b.sent(); - return [3 /*break*/, 4]; - case 3: - e_1 = _b.sent(); - error = e_1; - if (error.code === types_1.ErrorCode.FileNotFoundOrNoAccess) { - logger.debug(" could not remove folder. It doesn't exist!"); - } - else { - // unknown error - throw error; - } - return [3 /*break*/, 4]; - case 4: - // navigate back to the root folder - return [4 /*yield*/, upDir(client, (_a = path.folders) === null || _a === void 0 ? void 0 : _a.length)]; - case 5: - // navigate back to the root folder - _b.sent(); - logger.info(" completed"); - return [2 /*return*/]; - } - }); - }); -} -function removeFile(client, filePath, logger) { - var _a; - return __awaiter(this, void 0, void 0, function () { - var path, e_2, error; - return __generator(this, function (_b) { - switch (_b.label) { - case 0: - logger.all("removing " + filePath + "..."); - path = getFileBreadcrumbs(filePath); - if (!(path.folders === null)) return [3 /*break*/, 1]; - logger.debug(" no need to change dir"); - return [3 /*break*/, 3]; - case 1: - logger.debug(" changing dir to " + path.folders.join("/")); - return [4 /*yield*/, client.ensureDir(path.folders.join("/"))]; - case 2: - _b.sent(); - logger.debug(" dir changed"); - _b.label = 3; - case 3: - if (!(path.file !== null)) return [3 /*break*/, 7]; - _b.label = 4; - case 4: - _b.trys.push([4, 6, , 7]); - logger.debug(" removing file " + path.file); - return [4 /*yield*/, client.remove(path.file)]; - case 5: - _b.sent(); - logger.debug(" file removed"); - return [3 /*break*/, 7]; - case 6: - e_2 = _b.sent(); - error = e_2; - if (error.code === types_1.ErrorCode.FileNotFoundOrNoAccess) { - logger.info(" could not remove file. It doesn't exist!"); - } - else { - // unknown error - throw error; - } - return [3 /*break*/, 7]; - case 7: - // navigate back to the root folder - return [4 /*yield*/, upDir(client, (_a = path.folders) === null || _a === void 0 ? void 0 : _a.length)]; - case 8: - // navigate back to the root folder - _b.sent(); - logger.info(" Completed"); - return [2 /*return*/]; - } - }); - }); -} -function createLocalState(localFiles, logger, args) { - logger.debug("Creating local state at " + args["local-dir"] + args["state-name"]); - fs_1.default.writeFileSync("" + args["local-dir"] + args["state-name"], JSON.stringify(localFiles, undefined, 4), { encoding: "utf8" }); - logger.debug("Local state created"); -} -function connect(client, args) { - return __awaiter(this, void 0, void 0, function () { - var secure, rejectUnauthorized; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - secure = false; - if (args.protocol === "ftps") { - secure = true; - } - else if (args.protocol === "ftps-legacy") { - secure = "implicit"; - } - rejectUnauthorized = args.security === "loose"; - return [4 /*yield*/, client.access({ - host: args.server, - user: args.username, - password: args.password, - port: args.port, - secure: secure, - secureOptions: { - rejectUnauthorized: rejectUnauthorized - } - })]; - case 1: - _a.sent(); - return [2 /*return*/]; - } - }); - }); -} -function getServerFiles(client, logger, args) { - return __awaiter(this, void 0, void 0, function () { - var serverFiles, e_3; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - _a.trys.push([0, 5, , 6]); - logger.debug("Navigating to server dir - " + args["server-dir"]); - return [4 /*yield*/, client.ensureDir(args["server-dir"])]; - case 1: - _a.sent(); - logger.debug("Server dir navigated to (or created)"); - if (!args["dangerous-clean-slate"]) return [3 /*break*/, 3]; - logger.all("------------------------------------------------------"); - logger.all("🗑️ Removing all files on the server because 'dangerous-clean-slate' was set, this will make the deployment very slow..."); - return [4 /*yield*/, client.clearWorkingDir()]; - case 2: - _a.sent(); - logger.all("Clear complete"); - throw new Error("nope"); - case 3: return [4 /*yield*/, downloadFileList(client, args["state-name"])]; - case 4: - serverFiles = _a.sent(); - logger.all("------------------------------------------------------"); - logger.all("Last published on \uD83D\uDCC5 " + new Date(serverFiles.generatedTime).toLocaleDateString(undefined, { weekday: "long", year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "numeric" })); - return [2 /*return*/, serverFiles]; - case 5: - e_3 = _a.sent(); - logger.all("------------------------------------------------------"); - logger.all("No file exists on the server \"" + args["state-name"] + "\" - this much be your first publish! \uD83C\uDF89"); - logger.all("The first publish will take a while... but once the initial sync is done only differences are published!"); - logger.all("If you get this message and its NOT your first publish, something is wrong."); - // set the server state to nothing, because we don't know what the server state is - return [2 /*return*/, { - description: types_1.syncFileDescription, - version: types_1.currentVersion, - generatedTime: new Date().getTime(), - data: [], - }]; - case 6: return [2 /*return*/]; - } - }); - }); -} -function getDefaultSettings(withoutDefaults) { - var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l; - return { - "server": withoutDefaults.server, - "username": withoutDefaults.username, - "password": withoutDefaults.password, - "port": (_a = withoutDefaults.port) !== null && _a !== void 0 ? _a : 21, - "protocol": (_b = withoutDefaults.protocol) !== null && _b !== void 0 ? _b : "ftp", - "local-dir": (_c = withoutDefaults["local-dir"]) !== null && _c !== void 0 ? _c : "./", - "server-dir": (_d = withoutDefaults["server-dir"]) !== null && _d !== void 0 ? _d : "./", - "state-name": (_e = withoutDefaults["state-name"]) !== null && _e !== void 0 ? _e : ".ftp-deploy-sync-state.json", - "dry-run": (_f = withoutDefaults["dry-run"]) !== null && _f !== void 0 ? _f : false, - "dangerous-clean-slate": (_g = withoutDefaults["dangerous-clean-slate"]) !== null && _g !== void 0 ? _g : false, - "include": (_h = withoutDefaults.include) !== null && _h !== void 0 ? _h : [], - "exclude": (_j = withoutDefaults.exclude) !== null && _j !== void 0 ? _j : exports.excludeDefaults, - "log-level": (_k = withoutDefaults["log-level"]) !== null && _k !== void 0 ? _k : "info", - "security": (_l = withoutDefaults.security) !== null && _l !== void 0 ? _l : "loose", - }; -} -function syncLocalToServer(client, diffs, logger, args) { - return __awaiter(this, void 0, void 0, function () { - var totalCount, _i, _a, file, _b, _c, file, _d, _e, file, _f, _g, file, _h, _j, file; - return __generator(this, function (_k) { - switch (_k.label) { - case 0: - totalCount = diffs.delete.length + diffs.upload.length + diffs.replace.length; - logger.all("------------------------------------------------------"); - logger.all("Making changes to " + totalCount + " " + utilities_1.pluralize(totalCount, "file", "files") + " to sync server state"); - logger.all("Uploading: " + pretty_bytes_1.default(diffs.sizeUpload) + " -- Deleting: " + pretty_bytes_1.default(diffs.sizeDelete) + " -- Replacing: " + pretty_bytes_1.default(diffs.sizeReplace)); - logger.all("------------------------------------------------------"); - _i = 0, _a = diffs.upload.filter(function (item) { return item.type === "folder"; }); - _k.label = 1; - case 1: - if (!(_i < _a.length)) return [3 /*break*/, 4]; - file = _a[_i]; - return [4 /*yield*/, createFolder(client, file.name, logger)]; - case 2: - _k.sent(); - _k.label = 3; - case 3: - _i++; - return [3 /*break*/, 1]; - case 4: - _b = 0, _c = diffs.upload.filter(function (item) { return item.type === "file"; }).filter(function (item) { return item.name !== args["state-name"]; }); - _k.label = 5; - case 5: - if (!(_b < _c.length)) return [3 /*break*/, 8]; - file = _c[_b]; - return [4 /*yield*/, uploadFile(client, file.name, logger)]; - case 6: - _k.sent(); - _k.label = 7; - case 7: - _b++; - return [3 /*break*/, 5]; - case 8: - _d = 0, _e = diffs.replace.filter(function (item) { return item.type === "file"; }).filter(function (item) { return item.name !== args["state-name"]; }); - _k.label = 9; - case 9: - if (!(_d < _e.length)) return [3 /*break*/, 12]; - file = _e[_d]; - // note: FTP will replace old files with new files. We run replacements after uploads to limit downtime - return [4 /*yield*/, uploadFile(client, file.name, logger, "replace")]; - case 10: - // note: FTP will replace old files with new files. We run replacements after uploads to limit downtime - _k.sent(); - _k.label = 11; - case 11: - _d++; - return [3 /*break*/, 9]; - case 12: - _f = 0, _g = diffs.delete.filter(function (item) { return item.type === "file"; }); - _k.label = 13; - case 13: - if (!(_f < _g.length)) return [3 /*break*/, 16]; - file = _g[_f]; - return [4 /*yield*/, removeFile(client, file.name, logger)]; - case 14: - _k.sent(); - _k.label = 15; - case 15: - _f++; - return [3 /*break*/, 13]; - case 16: - _h = 0, _j = diffs.delete.filter(function (item) { return item.type === "folder"; }); - _k.label = 17; - case 17: - if (!(_h < _j.length)) return [3 /*break*/, 20]; - file = _j[_h]; - return [4 /*yield*/, removeFolder(client, file.name, logger)]; - case 18: - _k.sent(); - _k.label = 19; - case 19: - _h++; - return [3 /*break*/, 17]; - case 20: - logger.all("------------------------------------------------------"); - logger.all("\uD83C\uDF89 Sync complete. Saving current server state to \"" + args["state-name"] + "\""); - return [4 /*yield*/, client.uploadFrom(args["state-name"], args["state-name"])]; - case 21: - _k.sent(); - return [2 /*return*/]; - } - }); - }); -} -function deploy(deployArgs) { - return __awaiter(this, void 0, void 0, function () { - var args, logger, timings, localFiles, client, totalBytesUploaded, serverFiles, diffTool, diffs, e_4, error_1, ftpError, error_2, uploadSpeed; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - args = getDefaultSettings(deployArgs); - logger = new utilities_1.Logger(args["log-level"]); - timings = new utilities_1.Timings(); - timings.start("total"); - // header - // todo allow swapping out library/version text based on if we are using action - logger.all("------------------------------------------------------"); - logger.all("\uD83D\uDE80 Thanks for using ftp-deploy version " + types_1.currentVersion + ". Let's deploy some stuff! "); - logger.all("------------------------------------------------------"); - logger.all("If you found this project helpful, please support it"); - logger.all("by giving it a \u2B50 on Github --> https://github.com/SamKirkland/FTP-Deploy-Action"); - timings.start("hash"); - return [4 /*yield*/, getLocalFiles(args)]; - case 1: - localFiles = _a.sent(); - timings.end("hash"); - createLocalState(localFiles, logger, args); - client = new ftp.Client(); - client.ftp.verbose = args["log-level"] === "debug"; - totalBytesUploaded = 0; - _a.label = 2; - case 2: - _a.trys.push([2, 13, 14, 15]); - timings.start("connecting"); - return [4 /*yield*/, connect(client, args)]; - case 3: - _a.sent(); - timings.end("connecting"); - _a.label = 4; - case 4: - _a.trys.push([4, 11, , 12]); - return [4 /*yield*/, getServerFiles(client, logger, args)]; - case 5: - serverFiles = _a.sent(); - diffTool = new HashDiff_1.HashDiff(); - diffs = diffTool.getDiffs(localFiles, serverFiles, logger); - totalBytesUploaded = diffs.sizeUpload + diffs.sizeReplace; - timings.start("upload"); - _a.label = 6; - case 6: - _a.trys.push([6, 8, 9, 10]); - return [4 /*yield*/, syncLocalToServer(client, diffs, logger, args)]; - case 7: - _a.sent(); - return [3 /*break*/, 10]; - case 8: - e_4 = _a.sent(); - if (e_4.code === types_1.ErrorCode.FileNameNotAllowed) { - logger.warn("Error 553 FileNameNotAllowed, you don't have access to upload that file"); - logger.warn(e_4); - process.exit(); - } - logger.warn(e_4); - process.exit(); - return [3 /*break*/, 10]; - case 9: - timings.end("upload"); - return [7 /*endfinally*/]; - case 10: return [3 /*break*/, 12]; - case 11: - error_1 = _a.sent(); - ftpError = error_1; - if (ftpError.code === types_1.ErrorCode.FileNotFoundOrNoAccess) { - logger.warn("Couldn't find file"); - } - logger.warn(ftpError); - return [3 /*break*/, 12]; - case 12: return [3 /*break*/, 15]; - case 13: - error_2 = _a.sent(); - errorHandling_1.prettyError(logger, args, error_2); - return [3 /*break*/, 15]; - case 14: - client.close(); - timings.end("total"); - return [7 /*endfinally*/]; - case 15: - uploadSpeed = pretty_bytes_1.default(totalBytesUploaded / (timings.getTime("upload") / 1000)); - // footer - logger.all("------------------------------------------------------"); - logger.all("Time spent hashing: " + timings.getTimeFormatted("hash")); - logger.all("Time spent connecting to server: " + timings.getTimeFormatted("connecting")); - logger.all("Time spent deploying: " + timings.getTimeFormatted("upload") + " (" + uploadSpeed + "/second)"); - logger.all("------------------------------------------------------"); - logger.all("Total time: " + timings.getTimeFormatted("total")); - logger.all("------------------------------------------------------"); - return [2 /*return*/]; - } - }); - }); -} -exports.deploy = deploy; - - -/***/ }), - -/***/ 622: -/***/ (function(module) { - -module.exports = __webpack_require__(622); - -/***/ }), - -/***/ 631: -/***/ (function(module) { - -module.exports = __webpack_require__(631); - -/***/ }), - -/***/ 669: -/***/ (function(module) { - -module.exports = __webpack_require__(669); - -/***/ }), - -/***/ 677: -/***/ (function(__unusedmodule, exports) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); - - -/***/ }), - -/***/ 704: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_146276__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.readdirSync = void 0; -const directory_reader_1 = __nested_webpack_require_146276__(918); -const for_each_1 = __nested_webpack_require_146276__(448); -const fs_1 = __nested_webpack_require_146276__(73); -const syncFacade = { fs: fs_1.syncFS, forEach: for_each_1.syncForEach }; -function readdirSync(dir, options) { - let reader = new directory_reader_1.DirectoryReader(dir, options, syncFacade); - let stream = reader.stream; - let results = []; - let data = stream.read(); - while (data !== null) { - results.push(data); - data = stream.read(); - } - return results; -} -exports.readdirSync = readdirSync; -//# sourceMappingURL=index.js.map - -/***/ }), - -/***/ 717: -/***/ (function(module, __unusedexports, __nested_webpack_require_147068__) { - -var concatMap = __nested_webpack_require_147068__(891); -var balanced = __nested_webpack_require_147068__(760); - -module.exports = expandTop; - -var escSlash = '\0SLASH'+Math.random()+'\0'; -var escOpen = '\0OPEN'+Math.random()+'\0'; -var escClose = '\0CLOSE'+Math.random()+'\0'; -var escComma = '\0COMMA'+Math.random()+'\0'; -var escPeriod = '\0PERIOD'+Math.random()+'\0'; - -function numeric(str) { - return parseInt(str, 10) == str - ? parseInt(str, 10) - : str.charCodeAt(0); -} - -function escapeBraces(str) { - return str.split('\\\\').join(escSlash) - .split('\\{').join(escOpen) - .split('\\}').join(escClose) - .split('\\,').join(escComma) - .split('\\.').join(escPeriod); -} - -function unescapeBraces(str) { - return str.split(escSlash).join('\\') - .split(escOpen).join('{') - .split(escClose).join('}') - .split(escComma).join(',') - .split(escPeriod).join('.'); -} - - -// Basically just str.split(","), but handling cases -// where we have nested braced sections, which should be -// treated as individual members, like {a,{b,c},d} -function parseCommaParts(str) { - if (!str) - return ['']; - - var parts = []; - var m = balanced('{', '}', str); - - if (!m) - return str.split(','); - - var pre = m.pre; - var body = m.body; - var post = m.post; - var p = pre.split(','); - - p[p.length-1] += '{' + body + '}'; - var postParts = parseCommaParts(post); - if (post.length) { - p[p.length-1] += postParts.shift(); - p.push.apply(p, postParts); - } - - parts.push.apply(parts, p); - - return parts; -} - -function expandTop(str) { - if (!str) - return []; - - // I don't know why Bash 4.3 does this, but it does. - // Anything starting with {} will have the first two bytes preserved - // but *only* at the top level, so {},a}b will not expand to anything, - // but a{},b}c will be expanded to [a}c,abc]. - // One could argue that this is a bug in Bash, but since the goal of - // this module is to match Bash's rules, we escape a leading {} - if (str.substr(0, 2) === '{}') { - str = '\\{\\}' + str.substr(2); - } - - return expand(escapeBraces(str), true).map(unescapeBraces); -} - -function identity(e) { - return e; -} - -function embrace(str) { - return '{' + str + '}'; -} -function isPadded(el) { - return /^-?0\d/.test(el); -} - -function lte(i, y) { - return i <= y; -} -function gte(i, y) { - return i >= y; -} - -function expand(str, isTop) { - var expansions = []; - - var m = balanced('{', '}', str); - if (!m || /\$$/.test(m.pre)) return [str]; - - var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); - var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); - var isSequence = isNumericSequence || isAlphaSequence; - var isOptions = m.body.indexOf(',') >= 0; - if (!isSequence && !isOptions) { - // {a},b} - if (m.post.match(/,.*\}/)) { - str = m.pre + '{' + m.body + escClose + m.post; - return expand(str); - } - return [str]; - } - - var n; - if (isSequence) { - n = m.body.split(/\.\./); - } else { - n = parseCommaParts(m.body); - if (n.length === 1) { - // x{{a,b}}y ==> x{a}y x{b}y - n = expand(n[0], false).map(embrace); - if (n.length === 1) { - var post = m.post.length - ? expand(m.post, false) - : ['']; - return post.map(function(p) { - return m.pre + n[0] + p; - }); - } - } - } - - // at this point, n is the parts, and we know it's not a comma set - // with a single entry. - - // no need to expand pre, since it is guaranteed to be free of brace-sets - var pre = m.pre; - var post = m.post.length - ? expand(m.post, false) - : ['']; - - var N; - - if (isSequence) { - var x = numeric(n[0]); - var y = numeric(n[1]); - var width = Math.max(n[0].length, n[1].length) - var incr = n.length == 3 - ? Math.abs(numeric(n[2])) - : 1; - var test = lte; - var reverse = y < x; - if (reverse) { - incr *= -1; - test = gte; - } - var pad = n.some(isPadded); - - N = []; - - for (var i = x; test(i, y); i += incr) { - var c; - if (isAlphaSequence) { - c = String.fromCharCode(i); - if (c === '\\') - c = ''; - } else { - c = String(i); - if (pad) { - var need = width - c.length; - if (need > 0) { - var z = new Array(need + 1).join('0'); - if (i < 0) - c = '-' + z + c.slice(1); - else - c = z + c; - } - } - } - N.push(c); - } - } else { - N = concatMap(n, function(el) { return expand(el, false) }); - } - - for (var j = 0; j < N.length; j++) { - for (var k = 0; k < post.length; k++) { - var expansion = pre + N[j] + post[k]; - if (!isTop || isSequence || expansion) - expansions.push(expansion); - } - } - - return expansions; -} - - - -/***/ }), - -/***/ 735: -/***/ (function(__unusedmodule, exports) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isPathFilter = exports.isFilterCriterion = exports._filters = void 0; -/** - * Symbol used to store the underlying filters of a `pathFilter()` function. - */ -exports._filters = Symbol("_filters"); -/** - * Determines whether the given value is a `FilterCriterion`. - */ -function isFilterCriterion(value) { - let type = typeof value; - return type === "string" || - type === "boolean" || - type === "function" || - value instanceof RegExp; -} -exports.isFilterCriterion = isFilterCriterion; -/** - * Determines whether the given value is one of our internal `pathFilter()` functions. - */ -function isPathFilter(value) { - let fn = value; - return fn && - typeof fn === "function" && - typeof fn[exports._filters] === "object"; -} -exports.isPathFilter = isPathFilter; -//# sourceMappingURL=util.js.map - -/***/ }), - -/***/ 739: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_152929__) { - -"use strict"; - -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Timer = exports.Timings = exports.pluralize = exports.Logger = void 0; -var pretty_ms_1 = __importDefault(__nested_webpack_require_152929__(127)); -var Logger = /** @class */ (function () { - function Logger(level) { - this.level = level !== null && level !== void 0 ? level : "info"; - } - Logger.prototype.all = function () { - var data = []; - for (var _i = 0; _i < arguments.length; _i++) { - data[_i] = arguments[_i]; - } - console.log.apply(console, data); - }; - Logger.prototype.warn = function () { - var data = []; - for (var _i = 0; _i < arguments.length; _i++) { - data[_i] = arguments[_i]; - } - if (this.level === "debug") { - return; - } - console.log.apply(console, data); - }; - Logger.prototype.info = function () { - var data = []; - for (var _i = 0; _i < arguments.length; _i++) { - data[_i] = arguments[_i]; - } - if (this.level === "warn") { - return; - } - console.log.apply(console, data); - }; - Logger.prototype.debug = function () { - var data = []; - for (var _i = 0; _i < arguments.length; _i++) { - data[_i] = arguments[_i]; - } - if (this.level !== "debug") { - return; - } - console.log.apply(console, data); - }; - return Logger; -}()); -exports.Logger = Logger; -function pluralize(count, singular, plural) { - if (count === 1) { - return singular; - } - return plural; -} -exports.pluralize = pluralize; -var Timings = /** @class */ (function () { - function Timings() { - this.timers = {}; - } - Timings.prototype.start = function (type) { - this.timers[type] = new Timer(); - this.timers[type].start(); - }; - Timings.prototype.end = function (type) { - this.timers[type].end(); - }; - Timings.prototype.getTime = function (type) { - var timer = this.timers[type]; - if (timer === undefined || timer.time === null) { - return 0; - } - return timer.time; - }; - Timings.prototype.getTimeFormatted = function (type) { - var timer = this.timers[type]; - if (timer === undefined || timer.time === null) { - return "💣 Failed"; - } - return pretty_ms_1.default(timer.time, { verbose: true }); - }; - return Timings; -}()); -exports.Timings = Timings; -var Timer = /** @class */ (function () { - function Timer() { - this.startTime = null; - this.endTime = null; - } - Timer.prototype.start = function () { - this.startTime = new Date().getTime(); - }; - Timer.prototype.end = function () { - this.endTime = new Date().getTime(); - }; - Object.defineProperty(Timer.prototype, "time", { - get: function () { - if (this.startTime === null || this.endTime === null) { - return null; - } - return this.endTime - this.startTime; - }, - enumerable: false, - configurable: true - }); - return Timer; -}()); -exports.Timer = Timer; - - -/***/ }), - -/***/ 747: -/***/ (function(module) { - -module.exports = __webpack_require__(747); - -/***/ }), - -/***/ 760: -/***/ (function(module) { - -"use strict"; - -module.exports = balanced; -function balanced(a, b, str) { - if (a instanceof RegExp) a = maybeMatch(a, str); - if (b instanceof RegExp) b = maybeMatch(b, str); - - var r = range(a, b, str); - - return r && { - start: r[0], - end: r[1], - pre: str.slice(0, r[0]), - body: str.slice(r[0] + a.length, r[1]), - post: str.slice(r[1] + b.length) - }; -} - -function maybeMatch(reg, str) { - var m = str.match(reg); - return m ? m[0] : null; -} - -balanced.range = range; -function range(a, b, str) { - var begs, beg, left, right, result; - var ai = str.indexOf(a); - var bi = str.indexOf(b, ai + 1); - var i = ai; - - if (ai >= 0 && bi > 0) { - begs = []; - left = str.length; - - while (i >= 0 && !result) { - if (i == ai) { - begs.push(i); - ai = str.indexOf(a, i + 1); - } else if (begs.length == 1) { - result = [ begs.pop(), bi ]; - } else { - beg = begs.pop(); - if (beg < left) { - left = beg; - right = bi; - } - - bi = str.indexOf(b, i + 1); - } - - i = ai < bi && ai >= 0 ? ai : bi; - } - - if (begs.length) { - result = [ left, right ]; - } - } - - return result; -} - - -/***/ }), - -/***/ 803: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_157755__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); +Object.defineProperty(exports, "__esModule", ({ value: true })); exports.downloadTo = exports.uploadFrom = exports.connectForPassiveTransfer = exports.parsePasvResponse = exports.enterPassiveModeIPv4 = exports.parseEpsvResponse = exports.enterPassiveModeIPv6 = void 0; -const netUtils_1 = __nested_webpack_require_157755__(288); -const tls_1 = __nested_webpack_require_157755__(16); -const parseControlResponse_1 = __nested_webpack_require_157755__(948); +const netUtils_1 = __nested_webpack_require_120198__(6288); +const tls_1 = __nested_webpack_require_120198__(4016); +const parseControlResponse_1 = __nested_webpack_require_120198__(9948); /** * Prepare a data socket using passive mode over IPv6. */ @@ -4732,156 +4032,216 @@ function isWritableFinished(stream) { /***/ }), -/***/ 811: -/***/ (function(module, exports, __nested_webpack_require_171232__) { +/***/ 3717: +/***/ ((module, __unused_webpack_exports, __nested_webpack_require_133694__) => { -"use strict"; +var concatMap = __nested_webpack_require_133694__(6891); +var balanced = __nested_webpack_require_133694__(9417); -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.readdir = void 0; -const async_1 = __nested_webpack_require_171232__(833); -const iterator_1 = __nested_webpack_require_171232__(944); -const stream_1 = __nested_webpack_require_171232__(521); -const sync_1 = __nested_webpack_require_171232__(704); -const readdir = async_1.readdirAsync; -exports.readdir = readdir; -readdir.sync = sync_1.readdirSync; -readdir.async = async_1.readdirAsync; -readdir.stream = stream_1.readdirStream; -readdir.iterator = iterator_1.readdirIterator; -var async_2 = __nested_webpack_require_171232__(833); -Object.defineProperty(exports, "readdirAsync", { enumerable: true, get: function () { return async_2.readdirAsync; } }); -var iterator_2 = __nested_webpack_require_171232__(944); -Object.defineProperty(exports, "readdirIterator", { enumerable: true, get: function () { return iterator_2.readdirIterator; } }); -var stream_2 = __nested_webpack_require_171232__(521); -Object.defineProperty(exports, "readdirStream", { enumerable: true, get: function () { return stream_2.readdirStream; } }); -var sync_2 = __nested_webpack_require_171232__(704); -Object.defineProperty(exports, "readdirSync", { enumerable: true, get: function () { return sync_2.readdirSync; } }); -__exportStar(__nested_webpack_require_171232__(299), exports); -exports.default = readdir; -// CommonJS default export hack -/* eslint-env commonjs */ -if ( true && typeof module.exports === "object") { - module.exports = Object.assign(module.exports.default, module.exports); +module.exports = expandTop; + +var escSlash = '\0SLASH'+Math.random()+'\0'; +var escOpen = '\0OPEN'+Math.random()+'\0'; +var escClose = '\0CLOSE'+Math.random()+'\0'; +var escComma = '\0COMMA'+Math.random()+'\0'; +var escPeriod = '\0PERIOD'+Math.random()+'\0'; + +function numeric(str) { + return parseInt(str, 10) == str + ? parseInt(str, 10) + : str.charCodeAt(0); } -//# sourceMappingURL=index.js.map -/***/ }), - -/***/ 816: -/***/ (function(module) { - -"use strict"; - -module.exports = milliseconds => { - if (typeof milliseconds !== 'number') { - throw new TypeError('Expected a number'); - } - - const roundTowardsZero = milliseconds > 0 ? Math.floor : Math.ceil; - - return { - days: roundTowardsZero(milliseconds / 86400000), - hours: roundTowardsZero(milliseconds / 3600000) % 24, - minutes: roundTowardsZero(milliseconds / 60000) % 60, - seconds: roundTowardsZero(milliseconds / 1000) % 60, - milliseconds: roundTowardsZero(milliseconds) % 1000, - microseconds: roundTowardsZero(milliseconds * 1000) % 1000, - nanoseconds: roundTowardsZero(milliseconds * 1e6) % 1000 - }; -}; - - -/***/ }), - -/***/ 833: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_173934__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.readdirAsync = void 0; -const fs = __nested_webpack_require_173934__(747); -const directory_reader_1 = __nested_webpack_require_173934__(918); -const for_each_1 = __nested_webpack_require_173934__(504); -const asyncFacade = { fs, forEach: for_each_1.asyncForEach }; -function readdirAsync(dir, options, callback) { - if (typeof options === "function") { - callback = options; - options = undefined; - } - let promise = new Promise((resolve, reject) => { - let results = []; - let reader = new directory_reader_1.DirectoryReader(dir, options, asyncFacade); - let stream = reader.stream; - stream.on("error", (err) => { - reject(err); - stream.pause(); - }); - stream.on("data", (result) => { - results.push(result); - }); - stream.on("end", () => { - resolve(results); - }); - }); - if (callback) { - promise.then((results) => callback(null, results), (err) => callback(err, undefined)); - } - else { - return promise; - } +function escapeBraces(str) { + return str.split('\\\\').join(escSlash) + .split('\\{').join(escOpen) + .split('\\}').join(escClose) + .split('\\,').join(escComma) + .split('\\.').join(escPeriod); } -exports.readdirAsync = readdirAsync; -//# sourceMappingURL=index.js.map -/***/ }), +function unescapeBraces(str) { + return str.split(escSlash).join('\\') + .split(escOpen).join('{') + .split(escClose).join('}') + .split(escComma).join(',') + .split(escPeriod).join('.'); +} -/***/ 865: -/***/ (function(module, __unusedexports, __nested_webpack_require_175190__) { -"use strict"; +// Basically just str.split(","), but handling cases +// where we have nested braced sections, which should be +// treated as individual members, like {a,{b,c},d} +function parseCommaParts(str) { + if (!str) + return ['']; -const minimatch = __nested_webpack_require_175190__(973); -const arrayUnion = __nested_webpack_require_175190__(600); -const arrayDiffer = __nested_webpack_require_175190__(554); -const arrify = __nested_webpack_require_175190__(546); + var parts = []; + var m = balanced('{', '}', str); -module.exports = (list, patterns, options = {}) => { - list = arrify(list); - patterns = arrify(patterns); + if (!m) + return str.split(','); - if (list.length === 0 || patterns.length === 0) { - return []; - } + var pre = m.pre; + var body = m.body; + var post = m.post; + var p = pre.split(','); - return patterns.reduce((result, pattern) => { - let process = arrayUnion; + p[p.length-1] += '{' + body + '}'; + var postParts = parseCommaParts(post); + if (post.length) { + p[p.length-1] += postParts.shift(); + p.push.apply(p, postParts); + } - if (pattern[0] === '!') { - pattern = pattern.slice(1); - process = arrayDiffer; - } + parts.push.apply(parts, p); + + return parts; +} + +function expandTop(str) { + if (!str) + return []; + + // I don't know why Bash 4.3 does this, but it does. + // Anything starting with {} will have the first two bytes preserved + // but *only* at the top level, so {},a}b will not expand to anything, + // but a{},b}c will be expanded to [a}c,abc]. + // One could argue that this is a bug in Bash, but since the goal of + // this module is to match Bash's rules, we escape a leading {} + if (str.substr(0, 2) === '{}') { + str = '\\{\\}' + str.substr(2); + } + + return expand(escapeBraces(str), true).map(unescapeBraces); +} + +function identity(e) { + return e; +} + +function embrace(str) { + return '{' + str + '}'; +} +function isPadded(el) { + return /^-?0\d/.test(el); +} + +function lte(i, y) { + return i <= y; +} +function gte(i, y) { + return i >= y; +} + +function expand(str, isTop) { + var expansions = []; + + var m = balanced('{', '}', str); + if (!m || /\$$/.test(m.pre)) return [str]; + + var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); + var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); + var isSequence = isNumericSequence || isAlphaSequence; + var isOptions = m.body.indexOf(',') >= 0; + if (!isSequence && !isOptions) { + // {a},b} + if (m.post.match(/,.*\}/)) { + str = m.pre + '{' + m.body + escClose + m.post; + return expand(str); + } + return [str]; + } + + var n; + if (isSequence) { + n = m.body.split(/\.\./); + } else { + n = parseCommaParts(m.body); + if (n.length === 1) { + // x{{a,b}}y ==> x{a}y x{b}y + n = expand(n[0], false).map(embrace); + if (n.length === 1) { + var post = m.post.length + ? expand(m.post, false) + : ['']; + return post.map(function(p) { + return m.pre + n[0] + p; + }); + } + } + } + + // at this point, n is the parts, and we know it's not a comma set + // with a single entry. + + // no need to expand pre, since it is guaranteed to be free of brace-sets + var pre = m.pre; + var post = m.post.length + ? expand(m.post, false) + : ['']; + + var N; + + if (isSequence) { + var x = numeric(n[0]); + var y = numeric(n[1]); + var width = Math.max(n[0].length, n[1].length) + var incr = n.length == 3 + ? Math.abs(numeric(n[2])) + : 1; + var test = lte; + var reverse = y < x; + if (reverse) { + incr *= -1; + test = gte; + } + var pad = n.some(isPadded); + + N = []; + + for (var i = x; test(i, y); i += incr) { + var c; + if (isAlphaSequence) { + c = String.fromCharCode(i); + if (c === '\\') + c = ''; + } else { + c = String(i); + if (pad) { + var need = width - c.length; + if (need > 0) { + var z = new Array(need + 1).join('0'); + if (i < 0) + c = '-' + z + c.slice(1); + else + c = z + c; + } + } + } + N.push(c); + } + } else { + N = concatMap(n, function(el) { return expand(el, false) }); + } + + for (var j = 0; j < N.length; j++) { + for (var k = 0; k < post.length; k++) { + var expansion = pre + N[j] + post[k]; + if (!isTop || isSequence || expansion) + expansions.push(expansion); + } + } + + return expansions; +} - return process(result, minimatch.match(list, pattern, options)); - }, []); -}; /***/ }), -/***/ 891: -/***/ (function(module) { +/***/ 6891: +/***/ ((module) => { module.exports = function (xs, fn) { var res = []; @@ -4900,528 +4260,156 @@ var isArray = Array.isArray || function (xs) { /***/ }), -/***/ 918: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_176286__) { +/***/ 7117: +/***/ ((module) => { -"use strict"; +module.exports = function (glob, opts) { + if (typeof glob !== 'string') { + throw new TypeError('Expected a string'); + } -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DirectoryReader = void 0; -const path = __nested_webpack_require_176286__(622); -const stream_1 = __nested_webpack_require_176286__(413); -const call_1 = __nested_webpack_require_176286__(188); -const normalize_options_1 = __nested_webpack_require_176286__(977); -const stat_1 = __nested_webpack_require_176286__(445); -/** - * Asynchronously reads the contents of a directory and streams the results - * via a `ReadableStream`. - * - * @internal - */ -class DirectoryReader { - /** - * @param dir - The absolute or relative directory path to read - * @param [options] - User-specified options, if any (see `normalizeOptions()`) - * @param facade - sync or async function implementations - * @param emit - Indicates whether the reader should emit "file", "directory", and "symlink" events. - */ - constructor(dir, options, facade, emit = false) { - this.options = normalize_options_1.normalizeOptions(options, facade, emit); - // Indicates whether we should keep reading - // This is set false if stream.Readable.push() returns false. - this.shouldRead = true; - // The directories to read - // (initialized with the top-level directory) - this.queue = [{ - path: dir, - basePath: this.options.basePath, - depth: 0 - }]; - // The number of directories that are currently being processed - this.pending = 0; - // The data that has been read, but not yet emitted - this.buffer = []; - this.stream = new stream_1.Readable({ objectMode: true }); - this.stream._read = () => { - // Start (or resume) reading - this.shouldRead = true; - // If we have data in the buffer, then send the next chunk - if (this.buffer.length > 0) { - this.pushFromBuffer(); - } - // If we have directories queued, then start processing the next one - if (this.queue.length > 0) { - this.readNextDirectory(); - } - this.checkForEOF(); - }; + var str = String(glob); + + // The regexp we are building, as a string. + var reStr = ""; + + // Whether we are matching so called "extended" globs (like bash) and should + // support single character matching, matching ranges of characters, group + // matching, etc. + var extended = opts ? !!opts.extended : false; + + // When globstar is _false_ (default), '/foo/*' is translated a regexp like + // '^\/foo\/.*$' which will match any string beginning with '/foo/' + // When globstar is _true_, '/foo/*' is translated to regexp like + // '^\/foo\/[^/]*$' which will match any string beginning with '/foo/' BUT + // which does not have a '/' to the right of it. + // E.g. with '/foo/*' these will match: '/foo/bar', '/foo/bar.txt' but + // these will not '/foo/bar/baz', '/foo/bar/baz.txt' + // Lastely, when globstar is _true_, '/foo/**' is equivelant to '/foo/*' when + // globstar is _false_ + var globstar = opts ? !!opts.globstar : false; + + // If we are doing extended matching, this boolean is true when we are inside + // a group (eg {*.html,*.js}), and false otherwise. + var inGroup = false; + + // RegExp flags (eg "i" ) to pass in to RegExp constructor. + var flags = opts && typeof( opts.flags ) === "string" ? opts.flags : ""; + + var c; + for (var i = 0, len = str.length; i < len; i++) { + c = str[i]; + + switch (c) { + case "/": + case "$": + case "^": + case "+": + case ".": + case "(": + case ")": + case "=": + case "!": + case "|": + reStr += "\\" + c; + break; + + case "?": + if (extended) { + reStr += "."; + break; + } + + case "[": + case "]": + if (extended) { + reStr += c; + break; + } + + case "{": + if (extended) { + inGroup = true; + reStr += "("; + break; + } + + case "}": + if (extended) { + inGroup = false; + reStr += ")"; + break; + } + + case ",": + if (inGroup) { + reStr += "|"; + break; + } + reStr += "\\" + c; + break; + + case "*": + // Move over all consecutive "*"'s. + // Also store the previous and next characters + var prevChar = str[i - 1]; + var starCount = 1; + while(str[i + 1] === "*") { + starCount++; + i++; + } + var nextChar = str[i + 1]; + + if (!globstar) { + // globstar is disabled, so treat any number of "*" as one + reStr += ".*"; + } else { + // globstar is enabled, so determine if this is a globstar segment + var isGlobstar = starCount > 1 // multiple "*"'s + && (prevChar === "/" || prevChar === undefined) // from the start of the segment + && (nextChar === "/" || nextChar === undefined) // to the end of the segment + + if (isGlobstar) { + // it's a globstar, so match zero or more path segments + reStr += "((?:[^/]*(?:\/|$))*)"; + i++; // move over the "/" + } else { + // it's not a globstar, so only match one path segment + reStr += "([^/]*)"; + } + } + break; + + default: + reStr += c; } - /** - * Reads the next directory in the queue - */ - readNextDirectory() { - let { facade } = this.options; - let dir = this.queue.shift(); - this.pending++; - // Read the directory listing - call_1.safeCall(facade.fs.readdir, dir.path, (err, items) => { - if (err) { - // fs.readdir threw an error - this.emit("error", err); - return this.finishedReadingDirectory(); - } - try { - // Process each item in the directory (simultaneously, if async) - facade.forEach(items, this.processItem.bind(this, dir), this.finishedReadingDirectory.bind(this, dir)); - } - catch (err2) { - // facade.forEach threw an error - // (probably because fs.readdir returned an invalid result) - this.emit("error", err2); - this.finishedReadingDirectory(); - } - }); - } - /** - * This method is called after all items in a directory have been processed. - * - * NOTE: This does not necessarily mean that the reader is finished, since there may still - * be other directories queued or pending. - */ - finishedReadingDirectory() { - this.pending--; - if (this.shouldRead) { - // If we have directories queued, then start processing the next one - if (this.queue.length > 0) { - this.readNextDirectory(); - } - this.checkForEOF(); - } - } - /** - * Determines whether the reader has finished processing all items in all directories. - * If so, then the "end" event is fired (via {@Readable#push}) - */ - checkForEOF() { - if (this.buffer.length === 0 && // The stuff we've already read - this.pending === 0 && // The stuff we're currently reading - this.queue.length === 0) { // The stuff we haven't read yet - // There's no more stuff! - this.stream.push(null); - } - } - /** - * Processes a single item in a directory. - * - * If the item is a directory, and `option.deep` is enabled, then the item will be added - * to the directory queue. - * - * If the item meets the filter criteria, then it will be emitted to the reader's stream. - * - * @param dir - A directory object from the queue - * @param item - The name of the item (name only, no path) - * @param done - A callback function that is called after the item has been processed - */ - processItem(dir, item, done) { - let stream = this.stream; - let options = this.options; - let itemPath = dir.basePath + item; - let fullPath = path.join(dir.path, item); - // If `options.deep` is a number, and we've already recursed to the max depth, - // then there's no need to check fs.Stats to know if it's a directory. - // If `options.deep` is a function, then we'll need fs.Stats - let maxDepthReached = dir.depth >= options.recurseDepth; - // Do we need to call `fs.stat`? - let needStats = !maxDepthReached || // we need the fs.Stats to know if it's a directory - options.stats || // the user wants fs.Stats objects returned - options.recurseFnNeedsStats || // we need fs.Stats for the recurse function - options.filterFnNeedsStats || // we need fs.Stats for the filter function - stream.listenerCount("file") || // we need the fs.Stats to know if it's a file - stream.listenerCount("directory") || // we need the fs.Stats to know if it's a directory - stream.listenerCount("symlink"); // we need the fs.Stats to know if it's a symlink - // If we don't need stats, then exit early - if (!needStats) { - if (this.filter({ path: itemPath })) { - this.pushOrBuffer({ data: itemPath }); - } - return done(); - } - // Get the fs.Stats object for this path - stat_1.stat(options.facade.fs, fullPath, (err, stats) => { - if (err) { - // fs.stat threw an error - this.emit("error", err); - return done(); - } - try { - // Add the item's path to the fs.Stats object - // The base of this path, and its separators are determined by the options - // (i.e. options.basePath and options.sep) - stats.path = itemPath; - // Add depth of the path to the fs.Stats object for use this in the filter function - stats.depth = dir.depth; - if (this.shouldRecurse(stats, maxDepthReached)) { - // Add this subdirectory to the queue - this.queue.push({ - path: fullPath, - basePath: itemPath + options.sep, - depth: dir.depth + 1, - }); - } - // Determine whether this item matches the filter criteria - if (this.filter(stats)) { - this.pushOrBuffer({ - data: options.stats ? stats : itemPath, - file: stats.isFile(), - directory: stats.isDirectory(), - symlink: stats.isSymbolicLink(), - }); - } - done(); - } - catch (err2) { - // An error occurred while processing the item - // (probably during a user-specified function, such as options.deep, options.filter, etc.) - this.emit("error", err2); - done(); - } - }); - } - /** - * Pushes the given chunk of data to the stream, or adds it to the buffer, - * depending on the state of the stream. - */ - pushOrBuffer(chunk) { - // Add the chunk to the buffer - this.buffer.push(chunk); - // If we're still reading, then immediately emit the next chunk in the buffer - // (which may or may not be the chunk that we just added) - if (this.shouldRead) { - this.pushFromBuffer(); - } - } - /** - * Immediately pushes the next chunk in the buffer to the reader's stream. - * The "data" event will always be fired (via `Readable.push()`). - * In addition, the "file", "directory", and/or "symlink" events may be fired, - * depending on the type of properties of the chunk. - */ - pushFromBuffer() { - let stream = this.stream; - let chunk = this.buffer.shift(); - // Stream the data - try { - this.shouldRead = stream.push(chunk.data); - } - catch (err) { - this.emit("error", err); - } - if (this.options.emit) { - // Also emit specific events, based on the type of chunk - chunk.file && this.emit("file", chunk.data); - chunk.symlink && this.emit("symlink", chunk.data); - chunk.directory && this.emit("directory", chunk.data); - } - } - /** - * Determines whether the given directory meets the user-specified recursion criteria. - * If the user didn't specify recursion criteria, then this function will default to true. - * - * @param stats - The directory's `Stats` object - * @param maxDepthReached - Whether we've already crawled the user-specified depth - */ - shouldRecurse(stats, maxDepthReached) { - let { recurseFn } = this.options; - if (maxDepthReached) { - // We've already crawled to the maximum depth. So no more recursion. - return false; - } - else if (!stats.isDirectory()) { - // It's not a directory. So don't try to crawl it. - return false; - } - else if (recurseFn) { - try { - // Run the user-specified recursion criteria - return !!recurseFn(stats); - } - catch (err) { - // An error occurred in the user's code. - // In Sync and Async modes, this will return an error. - // In Streaming mode, we emit an "error" event, but continue processing - this.emit("error", err); - } - } - else { - // No recursion function was specified, and we're within the maximum depth. - // So crawl this directory. - return true; - } - } - /** - * Determines whether the given item meets the user-specified filter criteria. - * If the user didn't specify a filter, then this function will always return true. - * - * @param stats - The item's `Stats` object, or an object with just a `path` property - */ - filter(stats) { - let { filterFn } = this.options; - if (filterFn) { - try { - // Run the user-specified filter function - return !!filterFn(stats); - } - catch (err) { - // An error occurred in the user's code. - // In Sync and Async modes, this will return an error. - // In Streaming mode, we emit an "error" event, but continue processing - this.emit("error", err); - } - } - else { - // No filter was specified, so match everything - return true; - } - } - /** - * Emits an event. If one of the event listeners throws an error, - * then an "error" event is emitted. - */ - emit(eventName, data) { - let stream = this.stream; - try { - stream.emit(eventName, data); - } - catch (err) { - if (eventName === "error") { - // Don't recursively emit "error" events. - // If the first one fails, then just throw - throw err; - } - else { - stream.emit("error", err); - } - } - } -} -exports.DirectoryReader = DirectoryReader; -//# sourceMappingURL=directory-reader.js.map + } -/***/ }), + // When regexp 'g' flag is specified don't + // constrain the regular expression with ^ & $ + if (!flags || !~flags.indexOf('g')) { + reStr = "^" + reStr + "$"; + } -/***/ 944: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_188523__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.readdirIterator = void 0; -const fs = __nested_webpack_require_188523__(747); -const for_each_1 = __nested_webpack_require_188523__(504); -const directory_reader_1 = __nested_webpack_require_188523__(918); -const pending_1 = __nested_webpack_require_188523__(553); -const iteratorFacade = { fs, forEach: for_each_1.asyncForEach }; -function readdirIterator(dir, options) { - let reader = new directory_reader_1.DirectoryReader(dir, options, iteratorFacade); - let stream = reader.stream; - let pendingValues = []; - let pendingReads = []; - let error; - let readable = false; - let done = false; - stream.on("error", function streamError(err) { - error = err; - stream.pause(); - fulfillPendingReads(); - }); - stream.on("end", function streamEnd() { - done = true; - fulfillPendingReads(); - }); - stream.on("readable", function streamReadable() { - readable = true; - fulfillPendingReads(); - }); - return { - [Symbol.asyncIterator]() { - return this; - }, - next() { - let pendingRead = pending_1.pending(); - pendingReads.push(pendingRead); - // eslint-disable-next-line @typescript-eslint/no-floating-promises - Promise.resolve().then(fulfillPendingReads); - return pendingRead.promise; - } - }; - function fulfillPendingReads() { - if (error) { - while (pendingReads.length > 0) { - let pendingRead = pendingReads.shift(); - pendingRead.reject(error); - } - } - else if (pendingReads.length > 0) { - while (pendingReads.length > 0) { - let pendingRead = pendingReads.shift(); - let value = getNextValue(); - if (value) { - pendingRead.resolve({ value }); - } - else if (done) { - pendingRead.resolve({ done, value }); - } - else { - pendingReads.unshift(pendingRead); - break; - } - } - } - } - function getNextValue() { - let value = pendingValues.shift(); - if (value) { - return value; - } - else if (readable) { - readable = false; - while (true) { - value = stream.read(); - if (value) { - pendingValues.push(value); - } - else { - break; - } - } - return pendingValues.shift(); - } - } -} -exports.readdirIterator = readdirIterator; -//# sourceMappingURL=index.js.map - -/***/ }), - -/***/ 948: -/***/ (function(__unusedmodule, exports) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.positiveIntermediate = exports.positiveCompletion = exports.isMultiline = exports.isSingleLine = exports.parseControlResponse = void 0; -const LF = "\n"; -/** - * Parse an FTP control response as a collection of messages. A message is a complete - * single- or multiline response. A response can also contain multiple multiline responses - * that will each be represented by a message. A response can also be incomplete - * and be completed on the next incoming data chunk for which case this function also - * describes a `rest`. This function converts all CRLF to LF. - */ -function parseControlResponse(text) { - const lines = text.split(/\r?\n/).filter(isNotBlank); - const messages = []; - let startAt = 0; - let tokenRegex; - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - // No group has been opened. - if (!tokenRegex) { - if (isMultiline(line)) { - // Open a group by setting an expected token. - const token = line.substr(0, 3); - tokenRegex = new RegExp(`^${token}(?:$| )`); - startAt = i; - } - else if (isSingleLine(line)) { - // Single lines can be grouped immediately. - messages.push(line); - } - } - // Group has been opened, expect closing token. - else if (tokenRegex.test(line)) { - tokenRegex = undefined; - messages.push(lines.slice(startAt, i + 1).join(LF)); - } - } - // The last group might not have been closed, report it as a rest. - const rest = tokenRegex ? lines.slice(startAt).join(LF) + LF : ""; - return { messages, rest }; -} -exports.parseControlResponse = parseControlResponse; -function isSingleLine(line) { - return /^\d\d\d(?:$| )/.test(line); -} -exports.isSingleLine = isSingleLine; -function isMultiline(line) { - return /^\d\d\d-/.test(line); -} -exports.isMultiline = isMultiline; -/** - * Return true if an FTP return code describes a positive completion. - */ -function positiveCompletion(code) { - return code >= 200 && code < 300; -} -exports.positiveCompletion = positiveCompletion; -/** - * Return true if an FTP return code describes a positive intermediate response. - */ -function positiveIntermediate(code) { - return code >= 300 && code < 400; -} -exports.positiveIntermediate = positiveIntermediate; -function isNotBlank(str) { - return str !== ""; -} - - -/***/ }), - -/***/ 957: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_193904__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); + return new RegExp(reStr, flags); }; -Object.defineProperty(exports, "__esModule", { value: true }); -/** - * Public API - */ -__exportStar(__nested_webpack_require_193904__(337), exports); -__exportStar(__nested_webpack_require_193904__(52), exports); -__exportStar(__nested_webpack_require_193904__(202), exports); -__exportStar(__nested_webpack_require_193904__(993), exports); -__exportStar(__nested_webpack_require_193904__(677), exports); -var transfer_1 = __nested_webpack_require_193904__(803); -Object.defineProperty(exports, "enterPassiveModeIPv4", { enumerable: true, get: function () { return transfer_1.enterPassiveModeIPv4; } }); -Object.defineProperty(exports, "enterPassiveModeIPv6", { enumerable: true, get: function () { return transfer_1.enterPassiveModeIPv6; } }); /***/ }), -/***/ 973: -/***/ (function(module, __unusedexports, __nested_webpack_require_195149__) { +/***/ 3973: +/***/ ((module, __unused_webpack_exports, __nested_webpack_require_142457__) => { module.exports = minimatch minimatch.Minimatch = Minimatch var path = { sep: '/' } try { - path = __nested_webpack_require_195149__(622) + path = __nested_webpack_require_142457__(5622) } catch (er) {} var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {} -var expand = __nested_webpack_require_195149__(717) +var expand = __nested_webpack_require_142457__(3717) var plTypes = { '!': { open: '(?:(?!(?:', close: '))[^/]*?)'}, @@ -6339,137 +5327,440 @@ function regExpEscape (s) { /***/ }), -/***/ 977: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_220592__) { +/***/ 4865: +/***/ ((module, __unused_webpack_exports, __nested_webpack_require_167907__) => { "use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.normalizeOptions = void 0; -const file_path_filter_1 = __nested_webpack_require_220592__(410); -const path = __nested_webpack_require_220592__(622); -/** - * Validates and normalizes the options argument - * - * @param [options] - User-specified options, if any - * @param facade - sync or async function implementations - * @param emit - Indicates whether the reader should emit "file", "directory", and "symlink" events. - * - * @internal - */ -function normalizeOptions(options, facade, emit) { - if (options === null || options === undefined) { - options = {}; - } - else if (typeof options !== "object") { - throw new TypeError("options must be an object"); - } - let sep = options.sep; - if (sep === null || sep === undefined) { - sep = path.sep; - } - else if (typeof sep !== "string") { - throw new TypeError("options.sep must be a string"); - } - let stats = Boolean(options.stats || options.withFileTypes); - let recurseDepth, recurseFn, recurseFnNeedsStats = false, deep = options.deep; - if (deep === null || deep === undefined) { - recurseDepth = 0; - } - else if (typeof deep === "boolean") { - recurseDepth = deep ? Infinity : 0; - } - else if (typeof deep === "number") { - if (deep < 0 || isNaN(deep)) { - throw new Error("options.deep must be a positive number"); - } - else if (Math.floor(deep) !== deep) { - throw new Error("options.deep must be an integer"); - } - else { - recurseDepth = deep; - } - } - else if (typeof deep === "function") { - // Recursion functions require a Stats object - recurseFnNeedsStats = true; - recurseDepth = Infinity; - recurseFn = deep; - } - else if (deep instanceof RegExp || (typeof deep === "string" && deep.length > 0)) { - recurseDepth = Infinity; - recurseFn = file_path_filter_1.createFilter({ map, sep }, deep); - } - else { - throw new TypeError("options.deep must be a boolean, number, function, regular expression, or glob pattern"); - } - let filterFn, filterFnNeedsStats = false, filter = options.filter; - if (filter !== null && filter !== undefined) { - if (typeof filter === "function") { - // Filter functions requres a Stats object - filterFnNeedsStats = true; - filterFn = filter; - } - else if (filter instanceof RegExp || - typeof filter === "boolean" || - (typeof filter === "string" && filter.length > 0)) { - filterFn = file_path_filter_1.createFilter({ map, sep }, filter); - } - else { - throw new TypeError("options.filter must be a boolean, function, regular expression, or glob pattern"); - } - } - let basePath = options.basePath; - if (basePath === null || basePath === undefined) { - basePath = ""; - } - else if (typeof basePath === "string") { - // Append a path separator to the basePath, if necessary - if (basePath && basePath.substr(-1) !== sep) { - basePath += sep; - } - } - else { - throw new TypeError("options.basePath must be a string"); - } - // Determine which facade methods to use - if (options.fs === null || options.fs === undefined) { - // The user didn't provide their own facades, so use our internal ones - } - else if (typeof options.fs === "object") { - // Merge the internal facade methods with the user-provided `fs` facades - facade = Object.assign({}, facade); - facade.fs = Object.assign({}, facade.fs, options.fs); - } - else { - throw new TypeError("options.fs must be an object"); - } - return { - recurseDepth, - recurseFn, - recurseFnNeedsStats, - filterFn, - filterFnNeedsStats, - stats, - sep, - basePath, - facade, - emit, - }; -} -exports.normalizeOptions = normalizeOptions; -/** - * Maps our modified fs.Stats objects to file paths - */ -function map(stats) { - return stats.path; -} -//# sourceMappingURL=normalize-options.js.map +const minimatch = __nested_webpack_require_167907__(3973); +const arrayUnion = __nested_webpack_require_167907__(9600); +const arrayDiffer = __nested_webpack_require_167907__(6554); +const arrify = __nested_webpack_require_167907__(1546); + +module.exports = (list, patterns, options = {}) => { + list = arrify(list); + patterns = arrify(patterns); + + if (list.length === 0 || patterns.length === 0) { + return []; + } + + return patterns.reduce((result, pattern) => { + let process = arrayUnion; + + if (pattern[0] === '!') { + pattern = pattern.slice(1); + process = arrayDiffer; + } + + return process(result, minimatch.match(list, pattern, options)); + }, []); +}; + /***/ }), -/***/ 993: -/***/ (function(__unusedmodule, exports, __nested_webpack_require_224841__) { +/***/ 7816: +/***/ ((module) => { + +"use strict"; + +module.exports = milliseconds => { + if (typeof milliseconds !== 'number') { + throw new TypeError('Expected a number'); + } + + const roundTowardsZero = milliseconds > 0 ? Math.floor : Math.ceil; + + return { + days: roundTowardsZero(milliseconds / 86400000), + hours: roundTowardsZero(milliseconds / 3600000) % 24, + minutes: roundTowardsZero(milliseconds / 60000) % 60, + seconds: roundTowardsZero(milliseconds / 1000) % 60, + milliseconds: roundTowardsZero(milliseconds) % 1000, + microseconds: roundTowardsZero(milliseconds * 1000) % 1000, + nanoseconds: roundTowardsZero(milliseconds * 1e6) % 1000 + }; +}; + + +/***/ }), + +/***/ 5168: +/***/ ((module) => { + +"use strict"; + + +const BYTE_UNITS = [ + 'B', + 'kB', + 'MB', + 'GB', + 'TB', + 'PB', + 'EB', + 'ZB', + 'YB' +]; + +const BIT_UNITS = [ + 'b', + 'kbit', + 'Mbit', + 'Gbit', + 'Tbit', + 'Pbit', + 'Ebit', + 'Zbit', + 'Ybit' +]; + +/* +Formats the given number using `Number#toLocaleString`. +- If locale is a string, the value is expected to be a locale-key (for example: `de`). +- If locale is true, the system default locale is used for translation. +- If no value for locale is specified, the number is returned unmodified. +*/ +const toLocaleString = (number, locale) => { + let result = number; + if (typeof locale === 'string') { + result = number.toLocaleString(locale); + } else if (locale === true) { + result = number.toLocaleString(); + } + + return result; +}; + +module.exports = (number, options) => { + if (!Number.isFinite(number)) { + throw new TypeError(`Expected a finite number, got ${typeof number}: ${number}`); + } + + options = Object.assign({bits: false}, options); + const UNITS = options.bits ? BIT_UNITS : BYTE_UNITS; + + if (options.signed && number === 0) { + return ' 0 ' + UNITS[0]; + } + + const isNegative = number < 0; + const prefix = isNegative ? '-' : (options.signed ? '+' : ''); + + if (isNegative) { + number = -number; + } + + if (number < 1) { + const numberString = toLocaleString(number, options.locale); + return prefix + numberString + ' ' + UNITS[0]; + } + + const exponent = Math.min(Math.floor(Math.log10(number) / 3), UNITS.length - 1); + // eslint-disable-next-line unicorn/prefer-exponentiation-operator + number = Number((number / Math.pow(1000, exponent)).toPrecision(3)); + const numberString = toLocaleString(number, options.locale); + + const unit = UNITS[exponent]; + + return prefix + numberString + ' ' + unit; +}; + + +/***/ }), + +/***/ 1127: +/***/ ((module, __unused_webpack_exports, __nested_webpack_require_171036__) => { + +"use strict"; + +const parseMilliseconds = __nested_webpack_require_171036__(7816); + +const pluralize = (word, count) => count === 1 ? word : `${word}s`; + +const SECOND_ROUNDING_EPSILON = 0.0000001; + +module.exports = (milliseconds, options = {}) => { + if (!Number.isFinite(milliseconds)) { + throw new TypeError('Expected a finite number'); + } + + if (options.colonNotation) { + options.compact = false; + options.formatSubMilliseconds = false; + options.separateMilliseconds = false; + options.verbose = false; + } + + if (options.compact) { + options.secondsDecimalDigits = 0; + options.millisecondsDecimalDigits = 0; + } + + const result = []; + + const floorDecimals = (value, decimalDigits) => { + const flooredInterimValue = Math.floor((value * (10 ** decimalDigits)) + SECOND_ROUNDING_EPSILON); + const flooredValue = Math.round(flooredInterimValue) / (10 ** decimalDigits); + return flooredValue.toFixed(decimalDigits); + }; + + const add = (value, long, short, valueString) => { + if ((result.length === 0 || !options.colonNotation) && value === 0 && !(options.colonNotation && short === 'm')) { + return; + } + + valueString = (valueString || value || '0').toString(); + let prefix; + let suffix; + if (options.colonNotation) { + prefix = result.length > 0 ? ':' : ''; + suffix = ''; + const wholeDigits = valueString.includes('.') ? valueString.split('.')[0].length : valueString.length; + const minLength = result.length > 0 ? 2 : 1; + valueString = '0'.repeat(Math.max(0, minLength - wholeDigits)) + valueString; + } else { + prefix = ''; + suffix = options.verbose ? ' ' + pluralize(long, value) : short; + } + + result.push(prefix + valueString + suffix); + }; + + const parsed = parseMilliseconds(milliseconds); + + add(Math.trunc(parsed.days / 365), 'year', 'y'); + add(parsed.days % 365, 'day', 'd'); + add(parsed.hours, 'hour', 'h'); + add(parsed.minutes, 'minute', 'm'); + + if ( + options.separateMilliseconds || + options.formatSubMilliseconds || + milliseconds < 1000 + ) { + add(parsed.seconds, 'second', 's'); + if (options.formatSubMilliseconds) { + add(parsed.milliseconds, 'millisecond', 'ms'); + add(parsed.microseconds, 'microsecond', 'µs'); + add(parsed.nanoseconds, 'nanosecond', 'ns'); + } else { + const millisecondsAndBelow = + parsed.milliseconds + + (parsed.microseconds / 1000) + + (parsed.nanoseconds / 1e6); + + const millisecondsDecimalDigits = + typeof options.millisecondsDecimalDigits === 'number' ? + options.millisecondsDecimalDigits : + 0; + + const roundedMiliseconds = millisecondsAndBelow >= 1 ? + Math.round(millisecondsAndBelow) : + Math.ceil(millisecondsAndBelow); + + const millisecondsString = millisecondsDecimalDigits ? + millisecondsAndBelow.toFixed(millisecondsDecimalDigits) : + roundedMiliseconds; + + add( + Number.parseFloat(millisecondsString, 10), + 'millisecond', + 'ms', + millisecondsString + ); + } + } else { + const seconds = (milliseconds / 1000) % 60; + const secondsDecimalDigits = + typeof options.secondsDecimalDigits === 'number' ? + options.secondsDecimalDigits : + 1; + const secondsFixed = floorDecimals(seconds, secondsDecimalDigits); + const secondsString = options.keepDecimalsOnWholeSeconds ? + secondsFixed : + secondsFixed.replace(/\.0+$/, ''); + add(Number.parseFloat(secondsString, 10), 'second', 's', secondsString); + } + + if (result.length === 0) { + return '0' + (options.verbose ? ' milliseconds' : 'ms'); + } + + if (options.compact) { + return result[0]; + } + + if (typeof options.unitCount === 'number') { + const separator = options.colonNotation ? '' : ' '; + return result.slice(0, Math.max(options.unitCount, 1)).join(separator); + } + + return options.colonNotation ? result.join('') : result.join(' '); +}; + + +/***/ }), + +/***/ 3262: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.HashDiff = void 0; +function formatNumber(number) { + return number.toLocaleString(); +} +var HashDiff = /** @class */ (function () { + function HashDiff() { + } + HashDiff.prototype.getDiffs = function (localFiles, serverFiles, logger) { + var _a, _b, _c; + var uploadList = []; + var deleteList = []; + var replaceList = []; + var sizeUpload = 0; + var sizeDelete = 0; + var sizeReplace = 0; + // alphabetize each list based off path + var localFilesSorted = localFiles.data.sort(function (first, second) { return first.name.localeCompare(second.name); }); + var serverFilesSorted = serverFiles.data.sort(function (first, second) { return first.name.localeCompare(second.name); }); + logger.info("----------------------------------------------------------------"); + logger.info("Local Files:\t" + formatNumber(localFilesSorted.length)); + logger.info("Server Files:\t" + formatNumber(localFilesSorted.length)); + logger.info("----------------------------------------------------------------"); + logger.info("Calculating differences between client & server"); + logger.info("----------------------------------------------------------------"); + var localPosition = 0; + var serverPosition = 0; + while (localPosition + serverPosition < localFilesSorted.length + serverFilesSorted.length) { + var localFile = localFilesSorted[localPosition]; + var serverFile = serverFilesSorted[serverPosition]; + var fileNameCompare = 0; + if (localFile === undefined) { + fileNameCompare = 1; + } + if (serverFile === undefined) { + fileNameCompare = -1; + } + if (localFile !== undefined && serverFile !== undefined) { + fileNameCompare = localFile.name.localeCompare(serverFile.name); + } + if (fileNameCompare < 0) { + var icon = localFile.type === "folder" ? "\uD83D\uDCC1 Create" : "\u2795 Upload"; + logger.info(icon + ": " + localFile.name); + uploadList.push(localFile); + sizeUpload += (_a = localFile.size) !== null && _a !== void 0 ? _a : 0; + localPosition += 1; + } + else if (fileNameCompare > 0) { + var icon = serverFile.type === "folder" ? "\uD83D\uDCC1" : "\uD83D\uDDD1\uFE0F"; + logger.info(icon + " Delete: " + serverFile.name + " "); + deleteList.push(serverFile); + sizeDelete += (_b = serverFile.size) !== null && _b !== void 0 ? _b : 0; + serverPosition += 1; + } + else if (fileNameCompare === 0) { + // paths are a match + if (localFile.type === "file" && serverFile.type === "file") { + if (localFile.hash === serverFile.hash) { + logger.info("\u2696\uFE0F File content is the same, doing nothing: " + localFile.name); + } + else { + logger.info("\uD83D\uDD01 File replace: " + localFile.name); + sizeReplace += (_c = localFile.size) !== null && _c !== void 0 ? _c : 0; + replaceList.push(localFile); + } + } + localPosition += 1; + serverPosition += 1; + } + } + return { + upload: uploadList, + delete: deleteList, + replace: replaceList, + sizeDelete: sizeDelete, + sizeReplace: sizeReplace, + sizeUpload: sizeUpload + }; + }; + return HashDiff; +}()); +exports.HashDiff = HashDiff; + + +/***/ }), + +/***/ 78: +/***/ ((__unused_webpack_module, exports, __nested_webpack_require_178842__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.prettyError = void 0; +var types_1 = __nested_webpack_require_178842__(5077); +function outputOriginalErrorAndExit(logger, error) { + logger.all(); + logger.all("----------------------------------------------------------------"); + logger.all("---------------------- Full Error below ----------------------"); + logger.all(error); + process.exit(); +} +/** + * Converts a exception to helpful debug info + * @param error exception + */ +function prettyError(logger, args, error) { + logger.all(); + logger.all("----------------------------------------------------------------"); + logger.all("--------------- \uD83D\uDD25\uD83D\uDD25\uD83D\uDD25 A error occurred \uD83D\uDD25\uD83D\uDD25\uD83D\uDD25 --------------"); + logger.all("----------------------------------------------------------------"); + if (typeof error.code === "string") { + var errorCode = error.code; + if (errorCode === "ENOTFOUND") { + logger.warn("The server \"" + args.server + "\" doesn't seem to exist. Do you have a typo?"); + outputOriginalErrorAndExit(logger, error); + } + } + if (typeof error.name === "string") { + var errorName = error.name; + if (errorName.includes("ERR_TLS_CERT_ALTNAME_INVALID")) { + logger.warn("The certificate for \"" + args.server + "\" is likely shared. The host did not place your server on the list of valid domains for this cert."); + logger.warn("This is a common issue with shared hosts. You have a few options:"); + logger.warn(" - Ignore this error by setting security back to loose"); + logger.warn(" - Contact your hosting provider and ask them for your servers hostname"); + outputOriginalErrorAndExit(logger, error); + } + } + var ftpError = error; + if (typeof ftpError.code === "number") { + if (ftpError.code === types_1.ErrorCode.NotLoggedIn) { + var serverRequiresFTPS = ftpError.message.toLowerCase().includes("must use encryption"); + if (serverRequiresFTPS) { + logger.warn("The server you are connecting to requires encryption (ftps)"); + logger.warn("Enable FTPS by using the protocol option."); + outputOriginalErrorAndExit(logger, error); + } + else { + logger.warn("Could not login with the username \"" + args.username + "\" and password \"" + args.password + "\"."); + logger.warn("Make sure you can login with those credentials. If you have a space or a quote in your username or password be sure to escape them!"); + outputOriginalErrorAndExit(logger, error); + } + } + } + // unknown error :( + outputOriginalErrorAndExit(logger, error); +} +exports.prettyError = prettyError; + + +/***/ }), + +/***/ 6611: +/***/ (function(__unused_webpack_module, exports, __nested_webpack_require_181871__) { "use strict"; @@ -6492,174 +5783,6 @@ var __importStar = (this && this.__importStar) || function (mod) { __setModuleDefault(result, mod); return result; }; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.parseList = void 0; -const dosParser = __importStar(__nested_webpack_require_224841__(199)); -const unixParser = __importStar(__nested_webpack_require_224841__(375)); -const mlsdParser = __importStar(__nested_webpack_require_224841__(157)); -/** - * Available directory listing parsers. These are candidates that will be tested - * in the order presented. The first candidate will be used to parse the whole list. - */ -const availableParsers = [ - dosParser, - unixParser, - mlsdParser // Keep MLSD last, may accept filename only -]; -function firstCompatibleParser(line, parsers) { - return parsers.find(parser => parser.testLine(line) === true); -} -function stringIsNotBlank(str) { - return str.trim() !== ""; -} -const REGEX_NEWLINE = /\r?\n/; -/** - * Parse raw directory listing. - */ -function parseList(rawList) { - const lines = rawList - .split(REGEX_NEWLINE) - .filter(stringIsNotBlank); - if (lines.length === 0) { - return []; - } - const testLine = lines[lines.length - 1]; - const parser = firstCompatibleParser(testLine, availableParsers); - if (!parser) { - throw new Error("This library only supports MLSD, Unix- or DOS-style directory listing. Your FTP server seems to be using another format. You can see the transmitted listing when setting `client.ftp.verbose = true`. You can then provide a custom parser to `client.parseList`, see the documentation for details."); - } - const files = lines - .map(parser.parseLine) - .filter((info) => info !== undefined); - return parser.transformList(files); -} -exports.parseList = parseList; - - -/***/ }) - -/******/ }); - -/***/ }), - -/***/ 413: -/***/ (function(module) { - -module.exports = require("stream"); - -/***/ }), - -/***/ 417: -/***/ (function(module) { - -module.exports = require("crypto"); - -/***/ }), - -/***/ 431: -/***/ (function(__unusedmodule, exports, __webpack_require__) { - -"use strict"; - -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 os = __importStar(__webpack_require__(87)); -/** - * Commands - * - * Command Format: - * ::name key=value,key=value::message - * - * Examples: - * ::warning::This is the message - * ::set-env name=MY_VAR::some value - */ -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 += ' '; - let first = true; - for (const key in this.properties) { - if (this.properties.hasOwnProperty(key)) { - const val = this.properties[key]; - if (val) { - if (first) { - first = false; - } - else { - cmdStr += ','; - } - cmdStr += `${key}=${escapeProperty(val)}`; - } - } - } - } - cmdStr += `${CMD_STRING}${escapeData(this.message)}`; - return cmdStr; - } -} -/** - * Sanitizes an input into a string so it can be passed into issueCommand safely - * @param input input to sanitize into a string - */ -function toCommandValue(input) { - if (input === null || input === undefined) { - return ''; - } - else if (typeof input === 'string' || input instanceof String) { - return input; - } - return JSON.stringify(input); -} -exports.toCommandValue = toCommandValue; -function escapeData(s) { - return toCommandValue(s) - .replace(/%/g, '%25') - .replace(/\r/g, '%0D') - .replace(/\n/g, '%0A'); -} -function escapeProperty(s) { - return toCommandValue(s) - .replace(/%/g, '%25') - .replace(/\r/g, '%0D') - .replace(/\n/g, '%0A') - .replace(/:/g, '%3A') - .replace(/,/g, '%2C'); -} -//# sourceMappingURL=command.js.map - -/***/ }), - -/***/ 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) { @@ -6669,247 +5792,1187 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge 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 command_1 = __webpack_require__(431); -const os = __importStar(__webpack_require__(87)); -const path = __importStar(__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. Non-string values will be converted to a string via JSON.stringify - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function exportVariable(name, val) { - const convertedVal = command_1.toCommandValue(val); - process.env[name] = convertedVal; - command_1.issueCommand('set-env', { name }, convertedVal); -} -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}`); +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } - return val.trim(); -} -exports.getInput = getInput; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.deploy = exports.excludeDefaults = void 0; +var ftp = __importStar(__nested_webpack_require_181871__(7957)); +var readdir_enhanced_1 = __importDefault(__nested_webpack_require_181871__(8811)); +var crypto_1 = __importDefault(__nested_webpack_require_181871__(6417)); +var fs_1 = __importDefault(__nested_webpack_require_181871__(5747)); +var multiMatch_1 = __importDefault(__nested_webpack_require_181871__(4865)); +var stream_1 = __nested_webpack_require_181871__(2413); +var types_1 = __nested_webpack_require_181871__(5077); +var HashDiff_1 = __nested_webpack_require_181871__(3262); +var utilities_1 = __nested_webpack_require_181871__(5739); +var pretty_bytes_1 = __importDefault(__nested_webpack_require_181871__(5168)); +var errorHandling_1 = __nested_webpack_require_181871__(78); /** - * Sets the value of an output. - * - * @param name name of the output to set - * @param value value to store. Non-string values will be converted to a string via JSON.stringify + * Default excludes, ignores all git files and the node_modules folder */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function setOutput(name, value) { - command_1.issueCommand('set-output', { name }, value); -} -exports.setOutput = setOutput; -/** - * Enables or disables the echoing of commands into stdout for the rest of the step. - * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set. - * - */ -function setCommandEcho(enabled) { - command_1.issue('echo', enabled ? 'on' : 'off'); -} -exports.setCommandEcho = setCommandEcho; -//----------------------------------------------------------------------- -// 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 -//----------------------------------------------------------------------- -/** - * Gets whether Actions Step Debug is on or not - */ -function isDebug() { - return process.env['RUNNER_DEBUG'] === '1'; -} -exports.isDebug = isDebug; -/** - * 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. Errors will be converted to string via toString() - */ -function error(message) { - command_1.issue('error', message instanceof Error ? message.toString() : message); -} -exports.error = error; -/** - * Adds an warning issue - * @param message warning issue message. Errors will be converted to string via toString() - */ -function warning(message) { - command_1.issue('warning', message instanceof Error ? message.toString() : 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.excludeDefaults = [".git*", ".git*/**", "node_modules/**", "node_modules/**/*"]; +function fileHash(filename, algorithm) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, new Promise(function (resolve, reject) { + // Algorithm depends on availability of OpenSSL on platform + // Another algorithms: "sha1", "md5", "sha256", "sha512" ... + var shasum = crypto_1.default.createHash(algorithm); + try { + var s = fs_1.default.createReadStream(filename); + s.on("data", function (data) { + shasum.update(data); + }); + // making digest + s.on("end", function () { + var hash = shasum.digest("hex"); + return resolve(hash); + }); + } + catch (error) { + return reject("calc fail"); + } + })]; + }); }); } -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. Non-string values will be converted to a string via JSON.stringify - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function saveState(name, value) { - command_1.issueCommand('save-state', { name }, value); +// Excludes takes precedence over includes +function includeExcludeFilter(stat, args) { + // match exclude, return immediatley + if (args.exclude !== null) { + var exclude = multiMatch_1.default(stat.path, args.exclude, { matchBase: true, dot: true }); + if (exclude.length > 0) { + return false; + } + } + if (args.include !== null) { + // matches include - return immediatley + var include = multiMatch_1.default(stat.path, args.include, { matchBase: true, dot: true }); + if (include.length > 0) { + return true; + } + } + return true; } -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}`] || ''; +function getLocalFiles(args) { + return __awaiter(this, void 0, void 0, function () { + var files, records, _i, files_1, stat, _a, _b, _c; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: return [4 /*yield*/, readdir_enhanced_1.default.async(args["local-dir"], { deep: true, stats: true, sep: "/", filter: function (stat) { return includeExcludeFilter(stat, args); } })]; + case 1: + files = _d.sent(); + records = []; + _i = 0, files_1 = files; + _d.label = 2; + case 2: + if (!(_i < files_1.length)) return [3 /*break*/, 6]; + stat = files_1[_i]; + if (stat.isDirectory()) { + records.push({ + type: "folder", + name: stat.path, + size: undefined + }); + return [3 /*break*/, 5]; + } + if (!stat.isFile()) return [3 /*break*/, 4]; + _b = (_a = records).push; + _c = { + type: "file", + name: stat.path, + size: stat.size + }; + return [4 /*yield*/, fileHash(stat.path, "sha256")]; + case 3: + _b.apply(_a, [(_c.hash = _d.sent(), + _c)]); + return [3 /*break*/, 5]; + case 4: + if (stat.isSymbolicLink()) { + console.warn("Currently unable to handle symbolic links"); + } + _d.label = 5; + case 5: + _i++; + return [3 /*break*/, 2]; + case 6: return [2 /*return*/, { + description: types_1.syncFileDescription, + version: types_1.currentVersion, + generatedTime: new Date().getTime(), + data: records + }]; + } + }); + }); } -exports.getState = getState; -//# sourceMappingURL=core.js.map +function downloadFileList(client, path) { + return __awaiter(this, void 0, void 0, function () { + var _this = this; + return __generator(this, function (_a) { + return [2 /*return*/, new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () { + var downloadStream, chunks; + return __generator(this, function (_a) { + downloadStream = new stream_1.Stream.Writable(); + chunks = []; + downloadStream._write = function (chunk, encoding, next) { + chunks.push(chunk); + next(); + }; + downloadStream.on("error", reject); + downloadStream.on("finish", function () { + var file = Buffer.concat(chunks).toString("utf8"); + try { + resolve(JSON.parse(file)); + } + catch (e) { + reject(e); + } + }); + client.downloadTo(downloadStream, path).catch(function (reason) { + reject("Can't open due to: \"" + reason + "\""); + }); + return [2 /*return*/]; + }); + }); })]; + }); + }); +} +/** + * Converts a file path (ex: "folder/otherfolder/file.txt") to an array of folder and a file path + * @param fullPath + */ +function getFileBreadcrumbs(fullPath) { + var _a; + // todo see if this regex will work for nonstandard folder names + // todo what happens if the path is relative to the root dir? (starts with /) + var pathSplit = fullPath.split("/"); + var file = (_a = pathSplit === null || pathSplit === void 0 ? void 0 : pathSplit.pop()) !== null && _a !== void 0 ? _a : ""; // get last item + var folders = pathSplit.filter(function (folderName) { return folderName != ""; }); + return { + folders: folders.length === 0 ? null : folders, + file: file === "" ? null : file + }; +} +/** + * Navigates up {dirCount} number of directories from the current working dir + */ +function upDir(client, dirCount) { + return __awaiter(this, void 0, void 0, function () { + var i; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (typeof dirCount !== "number") { + return [2 /*return*/]; + } + i = 0; + _a.label = 1; + case 1: + if (!(i < dirCount)) return [3 /*break*/, 4]; + return [4 /*yield*/, client.cdup()]; + case 2: + _a.sent(); + _a.label = 3; + case 3: + i++; + return [3 /*break*/, 1]; + case 4: return [2 /*return*/]; + } + }); + }); +} +/** + * + * @param client ftp client + * @param file file can include folder(s) + * Note working dir is modified and NOT reset after upload + * For now we are going to reset it - but this will be removed for performance + */ +function uploadFile(client, filePath, logger, type) { + var _a; + if (type === void 0) { type = "upload"; } + return __awaiter(this, void 0, void 0, function () { + var typePresent, typePast, path; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + typePresent = type === "upload" ? "uploading" : "replacing"; + typePast = type === "upload" ? "uploaded" : "replaced"; + logger.all(typePresent + " \"" + filePath + "\""); + path = getFileBreadcrumbs(filePath); + if (!(path.folders === null)) return [3 /*break*/, 1]; + logger.debug(" no need to change dir"); + return [3 /*break*/, 3]; + case 1: + logger.debug(" changing dir to " + path.folders.join("/")); + return [4 /*yield*/, client.ensureDir(path.folders.join("/"))]; + case 2: + _b.sent(); + logger.debug(" dir changed"); + _b.label = 3; + case 3: + if (!(path.file !== null)) return [3 /*break*/, 5]; + logger.debug(" " + type + " started"); + return [4 /*yield*/, client.uploadFrom(filePath, path.file)]; + case 4: + _b.sent(); + logger.debug(" file " + typePast); + _b.label = 5; + case 5: + // navigate back to the root folder + return [4 /*yield*/, upDir(client, (_a = path.folders) === null || _a === void 0 ? void 0 : _a.length)]; + case 6: + // navigate back to the root folder + _b.sent(); + logger.debug(" completed"); + return [2 /*return*/]; + } + }); + }); +} +function createFolder(client, folderPath, logger) { + var _a; + return __awaiter(this, void 0, void 0, function () { + var path; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + logger.all("creating folder \"" + (folderPath + "/") + "\""); + path = getFileBreadcrumbs(folderPath + "/"); + if (!(path.folders === null)) return [3 /*break*/, 1]; + logger.debug(" no need to change dir"); + return [3 /*break*/, 3]; + case 1: + logger.debug(" creating folder " + path.folders.join("/")); + return [4 /*yield*/, client.ensureDir(path.folders.join("/"))]; + case 2: + _b.sent(); + _b.label = 3; + case 3: + // navigate back to the root folder + return [4 /*yield*/, upDir(client, (_a = path.folders) === null || _a === void 0 ? void 0 : _a.length)]; + case 4: + // navigate back to the root folder + _b.sent(); + logger.debug(" completed"); + return [2 /*return*/]; + } + }); + }); +} +function removeFolder(client, folderPath, logger) { + var _a; + return __awaiter(this, void 0, void 0, function () { + var path, e_1, error; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + logger.all("removing folder \"" + (folderPath + "/") + "\""); + path = getFileBreadcrumbs(folderPath + "/"); + if (!(path.folders === null)) return [3 /*break*/, 1]; + logger.debug(" no need to change dir"); + return [3 /*break*/, 4]; + case 1: + _b.trys.push([1, 3, , 4]); + logger.debug(" removing folder \"" + (path.folders.join("/") + "/") + "\""); + return [4 /*yield*/, client.removeDir(path.folders.join("/") + "/")]; + case 2: + _b.sent(); + return [3 /*break*/, 4]; + case 3: + e_1 = _b.sent(); + error = e_1; + if (error.code === types_1.ErrorCode.FileNotFoundOrNoAccess) { + logger.debug(" could not remove folder. It doesn't exist!"); + } + else { + // unknown error + throw error; + } + return [3 /*break*/, 4]; + case 4: + // navigate back to the root folder + return [4 /*yield*/, upDir(client, (_a = path.folders) === null || _a === void 0 ? void 0 : _a.length)]; + case 5: + // navigate back to the root folder + _b.sent(); + logger.debug(" completed"); + return [2 /*return*/]; + } + }); + }); +} +function removeFile(client, filePath, logger) { + var _a; + return __awaiter(this, void 0, void 0, function () { + var path, e_2, error; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + logger.all("removing " + filePath + "..."); + path = getFileBreadcrumbs(filePath); + if (!(path.folders === null)) return [3 /*break*/, 1]; + logger.debug(" no need to change dir"); + return [3 /*break*/, 3]; + case 1: + logger.debug(" changing dir to " + path.folders.join("/")); + return [4 /*yield*/, client.ensureDir(path.folders.join("/"))]; + case 2: + _b.sent(); + logger.debug(" dir changed"); + _b.label = 3; + case 3: + if (!(path.file !== null)) return [3 /*break*/, 7]; + _b.label = 4; + case 4: + _b.trys.push([4, 6, , 7]); + logger.debug(" removing file " + path.file); + return [4 /*yield*/, client.remove(path.file)]; + case 5: + _b.sent(); + logger.debug(" file removed"); + return [3 /*break*/, 7]; + case 6: + e_2 = _b.sent(); + error = e_2; + if (error.code === types_1.ErrorCode.FileNotFoundOrNoAccess) { + logger.info(" could not remove file. It doesn't exist!"); + } + else { + // unknown error + throw error; + } + return [3 /*break*/, 7]; + case 7: + // navigate back to the root folder + return [4 /*yield*/, upDir(client, (_a = path.folders) === null || _a === void 0 ? void 0 : _a.length)]; + case 8: + // navigate back to the root folder + _b.sent(); + logger.debug(" completed"); + return [2 /*return*/]; + } + }); + }); +} +function createLocalState(localFiles, logger, args) { + logger.debug("Creating local state at " + args["local-dir"] + args["state-name"]); + fs_1.default.writeFileSync("" + args["local-dir"] + args["state-name"], JSON.stringify(localFiles, undefined, 4), { encoding: "utf8" }); + logger.debug("Local state created"); +} +function connect(client, args) { + return __awaiter(this, void 0, void 0, function () { + var secure, rejectUnauthorized; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + secure = false; + if (args.protocol === "ftps") { + secure = true; + } + else if (args.protocol === "ftps-legacy") { + secure = "implicit"; + } + rejectUnauthorized = args.security === "loose"; + return [4 /*yield*/, client.access({ + host: args.server, + user: args.username, + password: args.password, + port: args.port, + secure: secure, + secureOptions: { + rejectUnauthorized: rejectUnauthorized + } + })]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); +} +function getServerFiles(client, logger, args) { + return __awaiter(this, void 0, void 0, function () { + var serverFiles, e_3; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _a.trys.push([0, 5, , 6]); + logger.debug("Navigating to server dir - " + args["server-dir"]); + return [4 /*yield*/, client.ensureDir(args["server-dir"])]; + case 1: + _a.sent(); + logger.debug("Server dir navigated to (or created)"); + if (!args["dangerous-clean-slate"]) return [3 /*break*/, 3]; + logger.all("----------------------------------------------------------------"); + logger.all("🗑️ Removing all files on the server because 'dangerous-clean-slate' was set, this will make the deployment very slow..."); + return [4 /*yield*/, client.clearWorkingDir()]; + case 2: + _a.sent(); + logger.all("Clear complete"); + throw new Error("nope"); + case 3: return [4 /*yield*/, downloadFileList(client, args["state-name"])]; + case 4: + serverFiles = _a.sent(); + logger.all("----------------------------------------------------------------"); + logger.all("Last published on \uD83D\uDCC5 " + new Date(serverFiles.generatedTime).toLocaleDateString(undefined, { weekday: "long", year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "numeric" })); + return [2 /*return*/, serverFiles]; + case 5: + e_3 = _a.sent(); + logger.all("----------------------------------------------------------------"); + logger.all("No file exists on the server \"" + args["state-name"] + "\" - this much be your first publish! \uD83C\uDF89"); + logger.all("The first publish will take a while... but once the initial sync is done only differences are published!"); + logger.all("If you get this message and its NOT your first publish, something is wrong."); + // set the server state to nothing, because we don't know what the server state is + return [2 /*return*/, { + description: types_1.syncFileDescription, + version: types_1.currentVersion, + generatedTime: new Date().getTime(), + data: [], + }]; + case 6: return [2 /*return*/]; + } + }); + }); +} +function getDefaultSettings(withoutDefaults) { + var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l; + return { + "server": withoutDefaults.server, + "username": withoutDefaults.username, + "password": withoutDefaults.password, + "port": (_a = withoutDefaults.port) !== null && _a !== void 0 ? _a : 21, + "protocol": (_b = withoutDefaults.protocol) !== null && _b !== void 0 ? _b : "ftp", + "local-dir": (_c = withoutDefaults["local-dir"]) !== null && _c !== void 0 ? _c : "./", + "server-dir": (_d = withoutDefaults["server-dir"]) !== null && _d !== void 0 ? _d : "./", + "state-name": (_e = withoutDefaults["state-name"]) !== null && _e !== void 0 ? _e : ".ftp-deploy-sync-state.json", + "dry-run": (_f = withoutDefaults["dry-run"]) !== null && _f !== void 0 ? _f : false, + "dangerous-clean-slate": (_g = withoutDefaults["dangerous-clean-slate"]) !== null && _g !== void 0 ? _g : false, + "include": (_h = withoutDefaults.include) !== null && _h !== void 0 ? _h : [], + "exclude": (_j = withoutDefaults.exclude) !== null && _j !== void 0 ? _j : exports.excludeDefaults, + "log-level": (_k = withoutDefaults["log-level"]) !== null && _k !== void 0 ? _k : "info", + "security": (_l = withoutDefaults.security) !== null && _l !== void 0 ? _l : "loose", + }; +} +function syncLocalToServer(client, diffs, logger, args) { + return __awaiter(this, void 0, void 0, function () { + var totalCount, _i, _a, file, _b, _c, file, _d, _e, file, _f, _g, file, _h, _j, file; + return __generator(this, function (_k) { + switch (_k.label) { + case 0: + totalCount = diffs.delete.length + diffs.upload.length + diffs.replace.length; + logger.all("----------------------------------------------------------------"); + logger.all("Making changes to " + totalCount + " " + utilities_1.pluralize(totalCount, "file", "files") + " to sync server state"); + logger.all("Uploading: " + pretty_bytes_1.default(diffs.sizeUpload) + " -- Deleting: " + pretty_bytes_1.default(diffs.sizeDelete) + " -- Replacing: " + pretty_bytes_1.default(diffs.sizeReplace)); + logger.all("----------------------------------------------------------------"); + _i = 0, _a = diffs.upload.filter(function (item) { return item.type === "folder"; }); + _k.label = 1; + case 1: + if (!(_i < _a.length)) return [3 /*break*/, 4]; + file = _a[_i]; + return [4 /*yield*/, createFolder(client, file.name, logger)]; + case 2: + _k.sent(); + _k.label = 3; + case 3: + _i++; + return [3 /*break*/, 1]; + case 4: + _b = 0, _c = diffs.upload.filter(function (item) { return item.type === "file"; }).filter(function (item) { return item.name !== args["state-name"]; }); + _k.label = 5; + case 5: + if (!(_b < _c.length)) return [3 /*break*/, 8]; + file = _c[_b]; + return [4 /*yield*/, uploadFile(client, file.name, logger)]; + case 6: + _k.sent(); + _k.label = 7; + case 7: + _b++; + return [3 /*break*/, 5]; + case 8: + _d = 0, _e = diffs.replace.filter(function (item) { return item.type === "file"; }).filter(function (item) { return item.name !== args["state-name"]; }); + _k.label = 9; + case 9: + if (!(_d < _e.length)) return [3 /*break*/, 12]; + file = _e[_d]; + // note: FTP will replace old files with new files. We run replacements after uploads to limit downtime + return [4 /*yield*/, uploadFile(client, file.name, logger, "replace")]; + case 10: + // note: FTP will replace old files with new files. We run replacements after uploads to limit downtime + _k.sent(); + _k.label = 11; + case 11: + _d++; + return [3 /*break*/, 9]; + case 12: + _f = 0, _g = diffs.delete.filter(function (item) { return item.type === "file"; }); + _k.label = 13; + case 13: + if (!(_f < _g.length)) return [3 /*break*/, 16]; + file = _g[_f]; + return [4 /*yield*/, removeFile(client, file.name, logger)]; + case 14: + _k.sent(); + _k.label = 15; + case 15: + _f++; + return [3 /*break*/, 13]; + case 16: + _h = 0, _j = diffs.delete.filter(function (item) { return item.type === "folder"; }); + _k.label = 17; + case 17: + if (!(_h < _j.length)) return [3 /*break*/, 20]; + file = _j[_h]; + return [4 /*yield*/, removeFolder(client, file.name, logger)]; + case 18: + _k.sent(); + _k.label = 19; + case 19: + _h++; + return [3 /*break*/, 17]; + case 20: + logger.all("----------------------------------------------------------------"); + logger.all("\uD83C\uDF89 Sync complete. Saving current server state to \"" + args["state-name"] + "\""); + return [4 /*yield*/, client.uploadFrom(args["state-name"], args["state-name"])]; + case 21: + _k.sent(); + return [2 /*return*/]; + } + }); + }); +} +function deploy(deployArgs) { + return __awaiter(this, void 0, void 0, function () { + var args, logger, timings, localFiles, client, totalBytesUploaded, serverFiles, diffTool, diffs, e_4, error_1, ftpError, error_2, uploadSpeed; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + args = getDefaultSettings(deployArgs); + logger = new utilities_1.Logger(args["log-level"]); + timings = new utilities_1.Timings(); + timings.start("total"); + // header + // todo allow swapping out library/version text based on if we are using action + logger.all("----------------------------------------------------------------"); + logger.all("\uD83D\uDE80 Thanks for using ftp-deploy version " + types_1.currentVersion + ". Let's deploy some stuff! "); + logger.all("----------------------------------------------------------------"); + logger.all("If you found this project helpful, please support it"); + logger.all("by giving it a \u2B50 on Github --> https://github.com/SamKirkland/FTP-Deploy-Action"); + logger.all("or add a badge \uD83C\uDFF7\uFE0F to your projects readme --> https://github.com/SamKirkland/FTP-Deploy-Action#badge"); + timings.start("hash"); + return [4 /*yield*/, getLocalFiles(args)]; + case 1: + localFiles = _a.sent(); + timings.end("hash"); + createLocalState(localFiles, logger, args); + client = new ftp.Client(); + client.ftp.verbose = args["log-level"] === "debug"; + totalBytesUploaded = 0; + _a.label = 2; + case 2: + _a.trys.push([2, 13, 14, 15]); + timings.start("connecting"); + return [4 /*yield*/, connect(client, args)]; + case 3: + _a.sent(); + timings.end("connecting"); + _a.label = 4; + case 4: + _a.trys.push([4, 11, , 12]); + return [4 /*yield*/, getServerFiles(client, logger, args)]; + case 5: + serverFiles = _a.sent(); + diffTool = new HashDiff_1.HashDiff(); + diffs = diffTool.getDiffs(localFiles, serverFiles, logger); + totalBytesUploaded = diffs.sizeUpload + diffs.sizeReplace; + timings.start("upload"); + _a.label = 6; + case 6: + _a.trys.push([6, 8, 9, 10]); + return [4 /*yield*/, syncLocalToServer(client, diffs, logger, args)]; + case 7: + _a.sent(); + return [3 /*break*/, 10]; + case 8: + e_4 = _a.sent(); + if (e_4.code === types_1.ErrorCode.FileNameNotAllowed) { + logger.warn("Error 553 FileNameNotAllowed, you don't have access to upload that file"); + logger.warn(e_4); + process.exit(); + } + logger.warn(e_4); + process.exit(); + return [3 /*break*/, 10]; + case 9: + timings.end("upload"); + return [7 /*endfinally*/]; + case 10: return [3 /*break*/, 12]; + case 11: + error_1 = _a.sent(); + ftpError = error_1; + if (ftpError.code === types_1.ErrorCode.FileNotFoundOrNoAccess) { + logger.warn("Couldn't find file"); + } + logger.warn(ftpError); + return [3 /*break*/, 12]; + case 12: return [3 /*break*/, 15]; + case 13: + error_2 = _a.sent(); + errorHandling_1.prettyError(logger, args, error_2); + return [3 /*break*/, 15]; + case 14: + client.close(); + timings.end("total"); + return [7 /*endfinally*/]; + case 15: + uploadSpeed = pretty_bytes_1.default(totalBytesUploaded / (timings.getTime("upload") / 1000)); + // footer + logger.all("----------------------------------------------------------------"); + logger.all("Time spent hashing: " + timings.getTimeFormatted("hash")); + logger.all("Time spent connecting to server: " + timings.getTimeFormatted("connecting")); + logger.all("Time spent deploying: " + timings.getTimeFormatted("upload") + " (" + uploadSpeed + "/second)"); + logger.all("----------------------------------------------------------------"); + logger.all("Total time: " + timings.getTimeFormatted("total")); + logger.all("----------------------------------------------------------------"); + return [2 /*return*/]; + } + }); + }); +} +exports.deploy = deploy; + /***/ }), -/***/ 622: -/***/ (function(module) { +/***/ 5077: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.ErrorCode = exports.syncFileDescription = exports.currentVersion = void 0; +exports.currentVersion = "1.0.0"; +exports.syncFileDescription = "DO NOT DELETE THIS FILE. This file is used to keep track of which files have been synced in the most recent deployment. If you delete this file a resync will need to be done (which can take a while) - read more: https://github.com/SamKirkland/FTP-Deploy-Action"; +var ErrorCode; +(function (ErrorCode) { + // The requested action is being initiated, expect another reply before proceeding with a new command. + ErrorCode[ErrorCode["RestartMarkerReplay"] = 110] = "RestartMarkerReplay"; + ErrorCode[ErrorCode["ServiceReadyInNNNMinutes"] = 120] = "ServiceReadyInNNNMinutes"; + ErrorCode[ErrorCode["DataConnectionAlreadyOpenStartingTransfer"] = 125] = "DataConnectionAlreadyOpenStartingTransfer"; + ErrorCode[ErrorCode["FileStatusOkayOpeningDataConnection"] = 150] = "FileStatusOkayOpeningDataConnection"; + // The requested action has been successfully completed. + ErrorCode[ErrorCode["CommandNotImplemented"] = 202] = "CommandNotImplemented"; + ErrorCode[ErrorCode["SystemStatus"] = 211] = "SystemStatus"; + ErrorCode[ErrorCode["DirectoryStatus"] = 212] = "DirectoryStatus"; + ErrorCode[ErrorCode["FileStatus"] = 213] = "FileStatus"; + ErrorCode[ErrorCode["HelpMessage"] = 214] = "HelpMessage"; + ErrorCode[ErrorCode["IANAOfficialName"] = 215] = "IANAOfficialName"; + ErrorCode[ErrorCode["ReadyForNewUser"] = 220] = "ReadyForNewUser"; + ErrorCode[ErrorCode["ClosingControlConnection"] = 221] = "ClosingControlConnection"; + ErrorCode[ErrorCode["DataConnectionOpen"] = 225] = "DataConnectionOpen"; + ErrorCode[ErrorCode["SuccessNowClosingDataConnection"] = 226] = "SuccessNowClosingDataConnection"; + ErrorCode[ErrorCode["EnteringPassiveMode"] = 227] = "EnteringPassiveMode"; + ErrorCode[ErrorCode["EnteringLongPassiveMode"] = 228] = "EnteringLongPassiveMode"; + ErrorCode[ErrorCode["EnteringExtendedPassiveMode"] = 229] = "EnteringExtendedPassiveMode"; + ErrorCode[ErrorCode["UserLoggedIn"] = 230] = "UserLoggedIn"; + ErrorCode[ErrorCode["UserLoggedOut"] = 231] = "UserLoggedOut"; + ErrorCode[ErrorCode["LogoutWillCompleteWhenTransferDone"] = 232] = "LogoutWillCompleteWhenTransferDone"; + ErrorCode[ErrorCode["ServerAcceptsAuthenticationMethod"] = 234] = "ServerAcceptsAuthenticationMethod"; + ErrorCode[ErrorCode["ActionComplete"] = 250] = "ActionComplete"; + ErrorCode[ErrorCode["PathNameCreated"] = 257] = "PathNameCreated"; + // The command has been accepted, but the requested action is on hold, pending receipt of further information. + ErrorCode[ErrorCode["UsernameOkayPasswordNeeded"] = 331] = "UsernameOkayPasswordNeeded"; + ErrorCode[ErrorCode["NeedAccountForLogin"] = 332] = "NeedAccountForLogin"; + ErrorCode[ErrorCode["RequestedFileActionPendingFurtherInformation"] = 350] = "RequestedFileActionPendingFurtherInformation"; + // The command was not accepted and the requested action did not take place, but the error condition is temporary and the action may be requested again. + ErrorCode[ErrorCode["ServiceNotAvailable"] = 421] = "ServiceNotAvailable"; + ErrorCode[ErrorCode["CantOpenDataConnection"] = 425] = "CantOpenDataConnection"; + ErrorCode[ErrorCode["ConnectionClosed"] = 426] = "ConnectionClosed"; + ErrorCode[ErrorCode["InvalidUsernameOrPassword"] = 430] = "InvalidUsernameOrPassword"; + ErrorCode[ErrorCode["HostUnavailable"] = 434] = "HostUnavailable"; + ErrorCode[ErrorCode["FileActionNotTaken"] = 450] = "FileActionNotTaken"; + ErrorCode[ErrorCode["LocalErrorProcessing"] = 451] = "LocalErrorProcessing"; + ErrorCode[ErrorCode["InsufficientStorageSpaceOrFileInUse"] = 452] = "InsufficientStorageSpaceOrFileInUse"; + // Syntax error, command unrecognized and the requested action did not take place. This may include errors such as command line too long. + ErrorCode[ErrorCode["SyntaxErrorInParameters"] = 501] = "SyntaxErrorInParameters"; + ErrorCode[ErrorCode["CommandNotImpemented"] = 502] = "CommandNotImpemented"; + ErrorCode[ErrorCode["BadSequenceOfCommands"] = 503] = "BadSequenceOfCommands"; + ErrorCode[ErrorCode["CommandNotImplementedForThatParameter"] = 504] = "CommandNotImplementedForThatParameter"; + ErrorCode[ErrorCode["NotLoggedIn"] = 530] = "NotLoggedIn"; + ErrorCode[ErrorCode["NeedAccountForStoringFiles"] = 532] = "NeedAccountForStoringFiles"; + ErrorCode[ErrorCode["CouldNotConnectToServerRequiresSSL"] = 534] = "CouldNotConnectToServerRequiresSSL"; + ErrorCode[ErrorCode["FileNotFoundOrNoAccess"] = 550] = "FileNotFoundOrNoAccess"; + ErrorCode[ErrorCode["UnknownPageType"] = 551] = "UnknownPageType"; + ErrorCode[ErrorCode["ExceededStorageAllocation"] = 552] = "ExceededStorageAllocation"; + ErrorCode[ErrorCode["FileNameNotAllowed"] = 553] = "FileNameNotAllowed"; + // Replies regarding confidentiality and integrity + ErrorCode[ErrorCode["IntegrityProtectedReply"] = 631] = "IntegrityProtectedReply"; + ErrorCode[ErrorCode["ConfidentialityAndIntegrityProtectedReply"] = 632] = "ConfidentialityAndIntegrityProtectedReply"; + ErrorCode[ErrorCode["ConfidentialityProtectedReply"] = 633] = "ConfidentialityProtectedReply"; + // Common Winsock Error Codes[2] (These are not FTP return codes) + ErrorCode[ErrorCode["ConnectionClosedByServer"] = 10054] = "ConnectionClosedByServer"; + ErrorCode[ErrorCode["CannotConnect"] = 10060] = "CannotConnect"; + ErrorCode[ErrorCode["CannotConnectRefusedByServer"] = 10061] = "CannotConnectRefusedByServer"; + ErrorCode[ErrorCode["DirectoryNotEmpty"] = 10066] = "DirectoryNotEmpty"; + ErrorCode[ErrorCode["TooManyUsers"] = 10068] = "TooManyUsers"; +})(ErrorCode = exports.ErrorCode || (exports.ErrorCode = {})); +; -module.exports = require("path"); /***/ }), -/***/ 631: -/***/ (function(module) { +/***/ 5739: +/***/ (function(__unused_webpack_module, exports, __nested_webpack_require_223229__) { + +"use strict"; + +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.Timer = exports.Timings = exports.pluralize = exports.Logger = void 0; +var pretty_ms_1 = __importDefault(__nested_webpack_require_223229__(1127)); +var Logger = /** @class */ (function () { + function Logger(level) { + this.level = level !== null && level !== void 0 ? level : "info"; + } + Logger.prototype.all = function () { + var data = []; + for (var _i = 0; _i < arguments.length; _i++) { + data[_i] = arguments[_i]; + } + console.log.apply(console, data); + }; + Logger.prototype.warn = function () { + var data = []; + for (var _i = 0; _i < arguments.length; _i++) { + data[_i] = arguments[_i]; + } + if (this.level === "debug") { + return; + } + console.log.apply(console, data); + }; + Logger.prototype.info = function () { + var data = []; + for (var _i = 0; _i < arguments.length; _i++) { + data[_i] = arguments[_i]; + } + if (this.level === "warn") { + return; + } + console.log.apply(console, data); + }; + Logger.prototype.debug = function () { + var data = []; + for (var _i = 0; _i < arguments.length; _i++) { + data[_i] = arguments[_i]; + } + if (this.level !== "debug") { + return; + } + console.log.apply(console, data); + }; + return Logger; +}()); +exports.Logger = Logger; +function pluralize(count, singular, plural) { + if (count === 1) { + return singular; + } + return plural; +} +exports.pluralize = pluralize; +var Timings = /** @class */ (function () { + function Timings() { + this.timers = {}; + } + Timings.prototype.start = function (type) { + this.timers[type] = new Timer(); + this.timers[type].start(); + }; + Timings.prototype.end = function (type) { + this.timers[type].end(); + }; + Timings.prototype.getTime = function (type) { + var timer = this.timers[type]; + if (timer === undefined || timer.time === null) { + return 0; + } + return timer.time; + }; + Timings.prototype.getTimeFormatted = function (type) { + var timer = this.timers[type]; + if (timer === undefined || timer.time === null) { + return "💣 Failed"; + } + return pretty_ms_1.default(timer.time, { verbose: true }); + }; + return Timings; +}()); +exports.Timings = Timings; +var Timer = /** @class */ (function () { + function Timer() { + this.startTime = null; + this.endTime = null; + } + Timer.prototype.start = function () { + this.startTime = new Date().getTime(); + }; + Timer.prototype.end = function () { + this.endTime = new Date().getTime(); + }; + Object.defineProperty(Timer.prototype, "time", { + get: function () { + if (this.startTime === null || this.endTime === null) { + return null; + } + return this.endTime - this.startTime; + }, + enumerable: false, + configurable: true + }); + return Timer; +}()); +exports.Timer = Timer; -module.exports = require("net"); /***/ }), -/***/ 669: -/***/ (function(module) { +/***/ 6417: +/***/ ((module) => { -module.exports = require("util"); +"use strict"; +module.exports = __webpack_require__(417); + +/***/ }), + +/***/ 5747: +/***/ ((module) => { + +"use strict"; +module.exports = __webpack_require__(747); + +/***/ }), + +/***/ 1631: +/***/ ((module) => { + +"use strict"; +module.exports = __webpack_require__(631); + +/***/ }), + +/***/ 5622: +/***/ ((module) => { + +"use strict"; +module.exports = __webpack_require__(622); + +/***/ }), + +/***/ 2413: +/***/ ((module) => { + +"use strict"; +module.exports = __webpack_require__(413); + +/***/ }), + +/***/ 4016: +/***/ ((module) => { + +"use strict"; +module.exports = __webpack_require__(16); + +/***/ }), + +/***/ 1669: +/***/ ((module) => { + +"use strict"; +module.exports = __webpack_require__(669); + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __nested_webpack_require_227574__(moduleId) { +/******/ // Check if module is in cache +/******/ if(__webpack_module_cache__[moduleId]) { +/******/ return __webpack_module_cache__[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ var threw = true; +/******/ try { +/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __nested_webpack_require_227574__); +/******/ threw = false; +/******/ } finally { +/******/ if(threw) delete __webpack_module_cache__[moduleId]; +/******/ } +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat */ +/******/ +/******/ __nested_webpack_require_227574__.ab = __dirname + "/";/************************************************************************/ +/******/ // module exports must be returned from runtime so entry inlining is disabled +/******/ // startup +/******/ // Load entry module and return exports +/******/ return __nested_webpack_require_227574__(6611); +/******/ })() +; + +/***/ }), + +/***/ 399: +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + +"use strict"; + +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +const core_1 = __importDefault(__webpack_require__(186)); +const ftp_deploy_1 = __webpack_require__(589); +async function runDeployment() { + const args = { + server: core_1.default.getInput("server", { required: true }), + username: core_1.default.getInput("username", { required: true }), + password: core_1.default.getInput("password", { required: true }), + protocol: optionalProtocol("protocol", core_1.default.getInput("protocol")), + port: optionalInt("port", core_1.default.getInput("port")), + "local-dir": core_1.default.getInput("local-dir"), + "server-dir": core_1.default.getInput("server-dir"), + "state-name": core_1.default.getInput("state-name"), + "dry-run": optionalBoolean("dry-run", core_1.default.getInput("dry-run")), + "dangerous-clean-slate": optionalBoolean("dangerous-clean-slate", core_1.default.getInput("dangerous-clean-slate")), + "include": optionalStringArray("include", core_1.default.getInput("include")), + "exclude": optionalStringArray("exclude", core_1.default.getInput("exclude")), + "log-level": optionalLogLevel("log-level", core_1.default.getInput("log-level")) + }; + try { + await ftp_deploy_1.deploy(args); + } + catch (error) { + core_1.default.setFailed(error); + } +} +runDeployment(); +function optionalBoolean(argumentName, rawValue) { + if (rawValue === undefined) { + return undefined; + } + const cleanValue = rawValue.toLowerCase(); + if (cleanValue === "true") { + return true; + } + if (cleanValue === "false") { + return false; + } + core_1.default.setFailed(`${argumentName}: invalid parameter - please use a boolean, you provided "${rawValue}". Try true or false instead.`); +} +function optionalProtocol(argumentName, rawValue) { + if (rawValue === undefined) { + return undefined; + } + const cleanValue = rawValue.toLowerCase(); + if (cleanValue === "ftp") { + return "ftp"; + } + if (cleanValue === "ftps") { + return "ftps"; + } + if (cleanValue === "ftps-legacy") { + return "ftps-legacy"; + } + core_1.default.setFailed(`${argumentName}: invalid parameter - you provided "${rawValue}". Try "ftp", "ftps", or "ftps-legacy" instead.`); +} +function optionalLogLevel(argumentName, rawValue) { + if (rawValue === undefined) { + return undefined; + } + const cleanValue = rawValue.toLowerCase(); + if (cleanValue === "warn") { + return "warn"; + } + if (cleanValue === "info") { + return "info"; + } + if (cleanValue === "debug") { + return "debug"; + } + core_1.default.setFailed(`${argumentName}: invalid parameter - you provided "${rawValue}". Try "warn", "info", or "debug" instead.`); +} +function optionalInt(argumentName, rawValue) { + if (rawValue === undefined) { + return undefined; + } + const cleanValue = rawValue.toLowerCase(); + const valueAsNumber = parseFloat(cleanValue); + if (Number.isInteger(valueAsNumber)) { + return valueAsNumber; + } + core_1.default.setFailed(`${argumentName}: invalid parameter - you provided "${rawValue}". Try a whole number (no decimals) instead like 1234`); +} +function optionalStringArray(argumentName, rawValue) { + if (rawValue === undefined) { + return undefined; + } + // split value by space and comma + return rawValue.split(", "); +} + + +/***/ }), + +/***/ 417: +/***/ ((module) => { + +"use strict"; +module.exports = require("crypto"); /***/ }), /***/ 747: -/***/ (function(module) { +/***/ ((module) => { +"use strict"; module.exports = require("fs"); +/***/ }), + +/***/ 631: +/***/ ((module) => { + +"use strict"; +module.exports = require("net"); + +/***/ }), + +/***/ 87: +/***/ ((module) => { + +"use strict"; +module.exports = require("os"); + +/***/ }), + +/***/ 622: +/***/ ((module) => { + +"use strict"; +module.exports = require("path"); + +/***/ }), + +/***/ 413: +/***/ ((module) => { + +"use strict"; +module.exports = require("stream"); + +/***/ }), + +/***/ 16: +/***/ ((module) => { + +"use strict"; +module.exports = require("tls"); + +/***/ }), + +/***/ 669: +/***/ ((module) => { + +"use strict"; +module.exports = require("util"); + /***/ }) -/******/ }); +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ if(__webpack_module_cache__[moduleId]) { +/******/ return __webpack_module_cache__[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ var threw = true; +/******/ try { +/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ threw = false; +/******/ } finally { +/******/ if(threw) delete __webpack_module_cache__[moduleId]; +/******/ } +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat */ +/******/ +/******/ __webpack_require__.ab = __dirname + "/";/************************************************************************/ +/******/ // module exports must be returned from runtime so entry inlining is disabled +/******/ // startup +/******/ // Load entry module and return exports +/******/ return __webpack_require__(399); +/******/ })() +; \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index f4e32e3..9dc33b2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,4 @@ -import * as core from "@actions/core"; +import core from "@actions/core"; import { deploy } from "@samkirkland/ftp-deploy"; import { IFtpDeployArguments } from "@samkirkland/ftp-deploy/dist/module/types"; @@ -7,16 +7,16 @@ async function runDeployment() { server: core.getInput("server", { required: true }), username: core.getInput("username", { required: true }), password: core.getInput("password", { required: true }), - protocol: core.getInput("protocol") as any, // todo fix - port: core.getInput("port") as any, // todo fix - "local-dir": core.getInput("local-dir") as any, // todo fix - "server-dir": core.getInput("server-dir") as any, // todo fix - "state-name": core.getInput("state-name") as any, // todo fix - "dry-run": core.getInput("dry-run") as any, // todo fix - "dangerous-clean-slate": core.getInput("dangerous-clean-slate") as any, // todo fix - "include": core.getInput("include") as any, // todo fix - "exclude": core.getInput("exclude") as any, // todo fix - "log-level": core.getInput("log-level") as any // todo fix + protocol: optionalProtocol("protocol", core.getInput("protocol")), + port: optionalInt("port", core.getInput("port")), + "local-dir": core.getInput("local-dir"), + "server-dir": core.getInput("server-dir"), + "state-name": core.getInput("state-name"), + "dry-run": optionalBoolean("dry-run", core.getInput("dry-run")), + "dangerous-clean-slate": optionalBoolean("dangerous-clean-slate", core.getInput("dangerous-clean-slate")), + "include": optionalStringArray("include", core.getInput("include")), + "exclude": optionalStringArray("exclude", core.getInput("exclude")), + "log-level": optionalLogLevel("log-level", core.getInput("log-level")) }; @@ -28,4 +28,90 @@ async function runDeployment() { } } -runDeployment(); \ No newline at end of file +runDeployment(); + + + + + + + + + +function optionalBoolean(argumentName: string, rawValue: string | undefined): boolean | undefined { + if (rawValue === undefined) { + return undefined; + } + + const cleanValue = rawValue.toLowerCase(); + if (cleanValue === "true") { + return true; + } + if (cleanValue === "false") { + return false; + } + + core.setFailed(`${argumentName}: invalid parameter - please use a boolean, you provided "${rawValue}". Try true or false instead.`); +} + +function optionalProtocol(argumentName: string, rawValue: string | undefined): "ftp" | "ftps" | "ftps-legacy" | undefined { + if (rawValue === undefined) { + return undefined; + } + + const cleanValue = rawValue.toLowerCase(); + if (cleanValue === "ftp") { + return "ftp"; + } + if (cleanValue === "ftps") { + return "ftps"; + } + if (cleanValue === "ftps-legacy") { + return "ftps-legacy"; + } + + core.setFailed(`${argumentName}: invalid parameter - you provided "${rawValue}". Try "ftp", "ftps", or "ftps-legacy" instead.`); +} + +function optionalLogLevel(argumentName: string, rawValue: string | undefined): "warn" | "info" | "debug" | undefined { + if (rawValue === undefined) { + return undefined; + } + + const cleanValue = rawValue.toLowerCase(); + if (cleanValue === "warn") { + return "warn"; + } + if (cleanValue === "info") { + return "info"; + } + if (cleanValue === "debug") { + return "debug"; + } + + core.setFailed(`${argumentName}: invalid parameter - you provided "${rawValue}". Try "warn", "info", or "debug" instead.`); +} + +function optionalInt(argumentName: string, rawValue: string | undefined): number | undefined { + if (rawValue === undefined) { + return undefined; + } + + const cleanValue = rawValue.toLowerCase(); + const valueAsNumber = parseFloat(cleanValue); + + if (Number.isInteger(valueAsNumber)) { + return valueAsNumber; + } + + core.setFailed(`${argumentName}: invalid parameter - you provided "${rawValue}". Try a whole number (no decimals) instead like 1234`); +} + +function optionalStringArray(argumentName: string, rawValue: string | undefined): string[] | undefined { + if (rawValue === undefined) { + return undefined; + } + + // split value by space and comma + return rawValue.split(", "); +}