From c14c34b02c9e68ac688eb29054fd2b98076bd128 Mon Sep 17 00:00:00 2001 From: SamKirkland Date: Fri, 23 Oct 2020 23:41:37 -0500 Subject: [PATCH] updated ftp-deploy version --- .eslintignore | 1 - .github/ISSUE_TEMPLATE/bug_report.md | 1 + .gitignore | 5 +- README.md | 25 +- action.yml | 11 +- dist/index.js | 6675 -------------------------- dist/main.js | 128 + package-lock.json | 26 +- package.json | 12 +- src/main.ts | 19 +- tsconfig.json | 4 +- 11 files changed, 169 insertions(+), 6738 deletions(-) delete mode 100644 dist/index.js create mode 100644 dist/main.js diff --git a/.eslintignore b/.eslintignore index 2186947..763301f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,2 @@ dist/ -lib/ node_modules/ \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2fb9d73..8651420 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -23,4 +23,5 @@ jobs: **My Action Log** ``` # Paste Log here + # you may want enable verbose logging with log-level: verbose ``` diff --git a/.gitignore b/.gitignore index 033af3f..cac7c97 100644 --- a/.gitignore +++ b/.gitignore @@ -92,7 +92,4 @@ typings/ # OS metadata .DS_Store -Thumbs.db - -# Ignore built ts files -lib/**/* \ No newline at end of file +Thumbs.db \ No newline at end of file diff --git a/README.md b/README.md index 4b9fbd1..ab02269 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,6 @@ 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 @@ -71,9 +68,8 @@ I strongly recommend you store your `password` as a secret. | `state-name` | No | `folder/.sync-state.json` | `.ftp-deploy-sync-state.json` | Path and name of the state file - this file is used to track which files have been deployed | | `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 | +| `log-level` | No | `minimal` | `standard` | `minimal`: only important info, `standard`: important info and basic file changes, `verbose`: print everything the script is doing | | `security` | No | `strict` | `loose` | `strict`: Reject any connection which is not authorized with the list of supplied CAs. `loose`: Allow connection even when the domain is not certificate | @@ -190,17 +186,14 @@ jobs: ``` +## Debugging your config locally +This action is a basic wrapper around my `@samkirkland/ftp-deploy` npm package. To test your config you can install [@samkirkland/ftp-deploy](https://github.com/SamKirkland/ftp-deploy) and then convert your config to a yml action. Settings are one-to-one, this action is only a wrapper. -## Debugging locally + +## Contributing to this project +To run this code locally you will need to setup docker and act to run a environment similar to the one github uses for actions. +- Download/install docker for windows, make sure it is running +- `choco install act-cli` install [act](https://github.com/nektos/act) - Install the npm package using `npm install --dev-only @samkirkland/ftp-deploy` -- Add a new key to your `package.json` file under `scripts` +- Update the `deploy` script in `package.json` with a actual server/username/password - You can run the script using the following command `npm run deploy` (run this in the folder that has the `package.json` file) - -Example of `package.json`: -```json -{ - "scripts": { - "deploy": "ftp-deploy --server ftp.samkirkland.com --username test@samkirkland.com --password \"CrazyUniquePassword&%123\"", - }, -} -``` diff --git a/action.yml b/action.yml index ed890e2..2eaca6b 100644 --- a/action.yml +++ b/action.yml @@ -11,12 +11,12 @@ inputs: password: required: true description: 'ftp password' - protocol: - required: false - description: 'protocol to deploy with - ftp, ftps, or ftps-legacy' port: required: false description: 'Server port to connect to (read your web hosts docs)' + protocol: + required: false + description: 'protocol to deploy with - ftp, ftps, or ftps-legacy' local-dir: required: false description: 'Folder to upload from, must end with trailing slash /' @@ -32,9 +32,6 @@ inputs: dangerous-clean-slate: required: false description: 'Deletes ALL contents of server-dir, even items in excluded with exclude argument' - include: - required: false - description: 'An array of glob patterns, these files will always be included in the publish/delete process - even if no change occurred' exclude: required: false description: 'An array of glob patterns, these files will not be included in the publish/delete process' @@ -46,7 +43,7 @@ inputs: description: 'strict or loose' runs: using: 'node12' - main: 'dist/index.js' + main: 'dist/main.js' branding: icon: 'upload-cloud' color: 'orange' \ No newline at end of file diff --git a/dist/index.js b/dist/index.js deleted file mode 100644 index d69c475..0000000 --- a/dist/index.js +++ /dev/null @@ -1,6675 +0,0 @@ -module.exports = -/******/ (() => { // webpackBootstrap -/******/ var __webpack_modules__ = ({ - -/***/ 7351: -/***/ (function(__unused_webpack_module, 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__(2087)); -/** - * 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 - -/***/ }), - -/***/ 2186: -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -const command_1 = __webpack_require__(7351); -const os = __importStar(__webpack_require__(2087)); -const path = __importStar(__webpack_require__(5622)); -/** - * 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 - -/***/ }), - -/***/ 5176: -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.createFilter = void 0; -const normalize_1 = __webpack_require__(7561); -const util_1 = __webpack_require__(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, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.filePathFilter = void 0; -const create_filter_1 = __webpack_require__(5176); -function filePathFilter(...args) { - return create_filter_1.createFilter({}, ...args); -} -exports.filePathFilter = filePathFilter; -//# sourceMappingURL=file-path-filter.js.map - -/***/ }), - -/***/ 3410: -/***/ (function(module, exports, __webpack_require__) { - -"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 = __webpack_require__(2405); -Object.defineProperty(exports, "filePathFilter", ({ enumerable: true, get: function () { return file_path_filter_1.filePathFilter; } })); -__exportStar(__webpack_require__(3225), exports); -var create_filter_1 = __webpack_require__(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); -} -//# sourceMappingURL=index.js.map - -/***/ }), - -/***/ 7561: -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.normalize = void 0; -const globToRegExp = __webpack_require__(7117); -const path = __webpack_require__(5622); -const util_1 = __webpack_require__(9735); -/** - * 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 - -/***/ }), - -/***/ 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.asyncForEach = asyncForEach; -//# sourceMappingURL=for-each.js.map - -/***/ }), - -/***/ 5833: -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.readdirAsync = void 0; -const fs = __webpack_require__(5747); -const directory_reader_1 = __webpack_require__(4918); -const for_each_1 = __webpack_require__(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; -/** - * 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 - -/***/ }), - -/***/ 4918: -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.DirectoryReader = void 0; -const path = __webpack_require__(5622); -const stream_1 = __webpack_require__(2413); -const call_1 = __webpack_require__(8188); -const normalize_options_1 = __webpack_require__(2977); -const stat_1 = __webpack_require__(9445); -/** - * 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(); - }; - } - /** - * 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 - -/***/ }), - -/***/ 8811: -/***/ (function(module, exports, __webpack_require__) { - -"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.readdir = void 0; -const async_1 = __webpack_require__(5833); -const iterator_1 = __webpack_require__(5944); -const stream_1 = __webpack_require__(5521); -const sync_1 = __webpack_require__(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 = __webpack_require__(5833); -Object.defineProperty(exports, "readdirAsync", ({ enumerable: true, get: function () { return async_2.readdirAsync; } })); -var iterator_2 = __webpack_require__(5944); -Object.defineProperty(exports, "readdirIterator", ({ enumerable: true, get: function () { return iterator_2.readdirIterator; } })); -var stream_2 = __webpack_require__(5521); -Object.defineProperty(exports, "readdirStream", ({ enumerable: true, get: function () { return stream_2.readdirStream; } })); -var sync_2 = __webpack_require__(704); -Object.defineProperty(exports, "readdirSync", ({ enumerable: true, get: function () { return sync_2.readdirSync; } })); -__exportStar(__webpack_require__(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, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.readdirIterator = void 0; -const fs = __webpack_require__(5747); -const for_each_1 = __webpack_require__(504); -const directory_reader_1 = __webpack_require__(4918); -const pending_1 = __webpack_require__(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, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.normalizeOptions = void 0; -const file_path_filter_1 = __webpack_require__(3410); -const path = __webpack_require__(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, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.stat = void 0; -const call_1 = __webpack_require__(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, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.readdirStream = void 0; -const fs = __webpack_require__(5747); -const for_each_1 = __webpack_require__(504); -const directory_reader_1 = __webpack_require__(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, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.syncFS = void 0; -const fs = __webpack_require__(5747); -const call_1 = __webpack_require__(8188); -/** - * Synchronous versions of `fs` methods. - * - * @internal - */ -exports.syncFS = { - /** - * A facade around `fs.readdirSync()` that allows it to be called - * the same way as `fs.readdir()`. - */ - readdir(dir, callback) { - // Make sure the callback is only called once - callback = call_1.callOnce(callback); - try { - let items = fs.readdirSync(dir); - callback(null, items); - } - catch (err) { - callback(err, undefined); - } - }, - /** - * A facade around `fs.statSync()` that allows it to be called - * the same way as `fs.stat()`. - */ - stat(path, callback) { - // Make sure the callback is only called once - callback = call_1.callOnce(callback); - try { - let stats = fs.statSync(path); - callback(null, stats); - } - catch (err) { - callback(err, undefined); - } - }, - /** - * A facade around `fs.lstatSync()` that allows it to be called - * the same way as `fs.lstat()`. - */ - lstat(path, callback) { - // Make sure the callback is only called once - callback = call_1.callOnce(callback); - try { - let stats = fs.lstatSync(path); - callback(null, stats); - } - catch (err) { - callback(err, undefined); - } - }, -}; -//# sourceMappingURL=fs.js.map - -/***/ }), - -/***/ 704: -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.readdirSync = void 0; -const directory_reader_1 = __webpack_require__(4918); -const for_each_1 = __webpack_require__(7448); -const fs_1 = __webpack_require__(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 - -/***/ }), - -/***/ 6299: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -//# sourceMappingURL=types-public.js.map - -/***/ }), - -/***/ 9946: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.HashDiff = void 0; -function formatNumber(number) { - return number.toLocaleString(); -} -class HashDiff { - getDiffs(localFiles, serverFiles, logger) { - var _a, _b, _c; - const uploadList = []; - const deleteList = []; - const replaceList = []; - let sizeUpload = 0; - let sizeDelete = 0; - let sizeReplace = 0; - // alphabetize each list based off path - const localFilesSorted = localFiles.data.sort((first, second) => first.name.localeCompare(second.name)); - const serverFilesSorted = serverFiles.data.sort((first, second) => 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(`----------------------------------------------------------------`); - let localPosition = 0; - let serverPosition = 0; - while (localPosition + serverPosition < localFilesSorted.length + serverFilesSorted.length) { - let localFile = localFilesSorted[localPosition]; - let serverFile = serverFilesSorted[serverPosition]; - let 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) { - let icon = localFile.type === "folder" ? `๐Ÿ“ Create` : `โž• 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) { - let icon = serverFile.type === "folder" ? `๐Ÿ“` : `๐Ÿ—‘๏ธ`; - 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(`โš–๏ธ File content is the same, doing nothing: ${localFile.name}`); - } - else { - logger.info(`๐Ÿ” 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, - sizeReplace, - sizeUpload - }; - } -} -exports.HashDiff = HashDiff; - - -/***/ }), - -/***/ 3678: -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.prettyError = void 0; -const types_1 = __webpack_require__(6703); -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(`--------------- ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ A error occurred ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ --------------`); - logger.all(`----------------------------------------------------------------`); - if (typeof error.code === "string") { - const 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") { - const 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); - } - } - const ftpError = error; - if (typeof ftpError.code === "number") { - if (ftpError.code === types_1.ErrorCode.NotLoggedIn) { - const 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; - - -/***/ }), - -/***/ 8347: -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { - -"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.prototype.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 __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; -const ftp = __importStar(__webpack_require__(7957)); -const readdir_enhanced_1 = __importDefault(__webpack_require__(8811)); -const crypto_1 = __importDefault(__webpack_require__(6417)); -const fs_1 = __importDefault(__webpack_require__(5747)); -const multiMatch_1 = __importDefault(__webpack_require__(4865)); -const stream_1 = __webpack_require__(2413); -const types_1 = __webpack_require__(6703); -const HashDiff_1 = __webpack_require__(9946); -const utilities_1 = __webpack_require__(4389); -const pretty_bytes_1 = __importDefault(__webpack_require__(5168)); -const errorHandling_1 = __webpack_require__(3678); -/** - * Default excludes, ignores all git files and the node_modules folder - */ -exports.excludeDefaults = [".git*", ".git*/**", "node_modules/**", "node_modules/**/*"]; -function fileHash(filename, algorithm) { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve, reject) => { - // Algorithm depends on availability of OpenSSL on platform - // Another algorithms: "sha1", "md5", "sha256", "sha512" ... - let shasum = crypto_1.default.createHash(algorithm); - try { - let s = fs_1.default.createReadStream(filename); - s.on("data", function (data) { - shasum.update(data); - }); - // making digest - s.on("end", function () { - const 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) { - const 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 - const 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* () { - const files = yield readdir_enhanced_1.default.async(args["local-dir"], { deep: true, stats: true, sep: "/", filter: (stat) => includeExcludeFilter(stat, args) }); - const records = []; - for (let stat of files) { - if (stat.isDirectory()) { - records.push({ - type: "folder", - name: stat.path, - size: undefined - }); - continue; - } - if (stat.isFile()) { - records.push({ - type: "file", - name: stat.path, - size: stat.size, - hash: yield fileHash(stat.path, "sha256") - }); - continue; - } - if (stat.isSymbolicLink()) { - console.warn("Currently unable to handle symbolic links"); - } - } - 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* () { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - const downloadStream = new stream_1.Stream.Writable(); - const chunks = []; - downloadStream._write = (chunk, encoding, next) => { - chunks.push(chunk); - next(); - }; - downloadStream.on("error", reject); - downloadStream.on("finish", () => { - const file = Buffer.concat(chunks).toString("utf8"); - try { - resolve(JSON.parse(file)); - } - catch (e) { - reject(e); - } - }); - client.downloadTo(downloadStream, path).catch((reason) => { - reject(`Can't open due to: "${reason}"`); - }); - })); - }); -} -/** - * 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 /) - const pathSplit = fullPath.split("/"); - const file = (_a = pathSplit === null || pathSplit === void 0 ? void 0 : pathSplit.pop()) !== null && _a !== void 0 ? _a : ""; // get last item - const folders = pathSplit.filter(folderName => 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* () { - if (typeof dirCount !== "number") { - return; - } - // navigate back to the starting folder - for (let i = 0; i < dirCount; i++) { - yield client.cdup(); - } - }); -} -function ensureDir(client, logger, timings, folder) { - return __awaiter(this, void 0, void 0, function* () { - timings.start("changingDir"); - logger.debug(` changing dir to ${folder}`); - yield client.ensureDir(folder); - logger.debug(` dir changed`); - timings.stop("changingDir"); - }); -} -/** - * - * @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, timings, type = "upload") { - return __awaiter(this, void 0, void 0, function* () { - const typePresent = type === "upload" ? "uploading" : "replacing"; - const typePast = type === "upload" ? "uploaded" : "replaced"; - logger.all(`${typePresent} "${filePath}"`); - yield client.uploadFrom(filePath, filePath); - logger.debug(` file ${typePast}`); - }); -} -function createFolder(client, folderPath, logger, timings) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - logger.all(`creating folder "${folderPath + "/"}"`); - const path = getFileBreadcrumbs(folderPath + "/"); - if (path.folders === null) { - logger.debug(` no need to change dir`); - } - else { - yield ensureDir(client, logger, timings, path.folders.join("/")); - } - // navigate back to the root folder - yield upDir(client, (_a = path.folders) === null || _a === void 0 ? void 0 : _a.length); - logger.debug(` completed`); - }); -} -function removeFolder(client, folderPath, logger) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - logger.all(`removing folder "${folderPath + "/"}"`); - const path = getFileBreadcrumbs(folderPath + "/"); - if (path.folders === null) { - logger.debug(` no need to change dir`); - } - else { - try { - logger.debug(` removing folder "${path.folders.join("/") + "/"}"`); - yield client.removeDir(path.folders.join("/") + "/"); - } - catch (e) { - let error = e; - if (error.code === types_1.ErrorCode.FileNotFoundOrNoAccess) { - logger.debug(` could not remove folder. It doesn't exist!`); - } - else { - // unknown error - throw error; - } - } - } - // navigate back to the root folder - yield upDir(client, (_a = path.folders) === null || _a === void 0 ? void 0 : _a.length); - logger.debug(` completed`); - }); -} -function removeFile(client, filePath, logger) { - return __awaiter(this, void 0, void 0, function* () { - logger.all(`removing ${filePath}...`); - try { - yield client.remove(filePath); - logger.debug(` file removed`); - } - catch (e) { - let error = e; - if (error.code === types_1.ErrorCode.FileNotFoundOrNoAccess) { - logger.info(` could not remove file. It doesn't exist!`); - } - else { - // unknown error - throw error; - } - } - logger.debug(` completed`); - }); -} -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* () { - let secure = false; - if (args.protocol === "ftps") { - secure = true; - } - else if (args.protocol === "ftps-legacy") { - secure = "implicit"; - } - const rejectUnauthorized = args.security === "loose"; - yield client.access({ - host: args.server, - user: args.username, - password: args.password, - port: args.port, - secure: secure, - secureOptions: { - rejectUnauthorized: rejectUnauthorized - } - }); - }); -} -function getServerFiles(client, logger, timings, args) { - return __awaiter(this, void 0, void 0, function* () { - try { - yield ensureDir(client, logger, timings, args["server-dir"]); - if (args["dangerous-clean-slate"]) { - logger.all(`----------------------------------------------------------------`); - logger.all("๐Ÿ—‘๏ธ Removing all files on the server because 'dangerous-clean-slate' was set, this will make the deployment very slow..."); - yield client.clearWorkingDir(); - logger.all("Clear complete"); - throw new Error("nope"); - } - const serverFiles = yield downloadFileList(client, args["state-name"]); - logger.all(`----------------------------------------------------------------`); - logger.all(`Last published on ๐Ÿ“… ${new Date(serverFiles.generatedTime).toLocaleDateString(undefined, { weekday: "long", year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "numeric" })}`); - return serverFiles; - } - catch (e) { - logger.all(`----------------------------------------------------------------`); - logger.all(`No file exists on the server "${args["server-dir"] + args["state-name"]}" - this much be your first publish! ๐ŸŽ‰`); - 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 { - description: types_1.syncFileDescription, - version: types_1.currentVersion, - generatedTime: new Date().getTime(), - data: [], - }; - } - }); -} -function getDefaultSettings(withoutDefaults) { - var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l; - if (withoutDefaults["local-dir"] !== undefined) { - if (!withoutDefaults["local-dir"].endsWith("/")) { - throw new Error("local-dir should be a folder (must end with /)"); - } - } - if (withoutDefaults["server-dir"] !== undefined) { - if (!withoutDefaults["server-dir"].endsWith("/")) { - throw new Error("server-dir should be a folder (must end with /)"); - } - } - 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, timings, args) { - return __awaiter(this, void 0, void 0, function* () { - const 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(`----------------------------------------------------------------`); - // create new folders - for (const file of diffs.upload.filter(item => item.type === "folder")) { - yield createFolder(client, file.name, logger, timings); - } - // upload new files - for (const file of diffs.upload.filter(item => item.type === "file").filter(item => item.name !== args["state-name"])) { - yield uploadFile(client, file.name, logger, timings); - } - // replace new files - for (const file of diffs.replace.filter(item => item.type === "file").filter(item => item.name !== args["state-name"])) { - // note: FTP will replace old files with new files. We run replacements after uploads to limit downtime - yield uploadFile(client, file.name, logger, timings, "replace"); - } - // delete old files - for (const file of diffs.delete.filter(item => item.type === "file")) { - yield removeFile(client, file.name, logger); - } - // delete old folders - for (const file of diffs.delete.filter(item => item.type === "folder")) { - yield removeFolder(client, file.name, logger); - } - logger.all(`----------------------------------------------------------------`); - logger.all(`๐ŸŽ‰ Sync complete. Saving current server state to "${args["server-dir"] + args["state-name"]}"`); - yield client.uploadFrom(args["state-name"], args["state-name"]); - }); -} -function deploy(deployArgs) { - return __awaiter(this, void 0, void 0, function* () { - const args = getDefaultSettings(deployArgs); - const logger = new utilities_1.Logger(args["log-level"]); - const 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(`๐Ÿš€ 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 โญ on Github --> https://github.com/SamKirkland/FTP-Deploy-Action`); - logger.all(`or add a badge ๐Ÿท๏ธ to your projects readme --> https://github.com/SamKirkland/FTP-Deploy-Action#badge`); - timings.start("hash"); - const localFiles = yield getLocalFiles(args); - timings.stop("hash"); - createLocalState(localFiles, logger, args); - const client = new ftp.Client(); - client.ftp.verbose = args["log-level"] === "debug"; - let totalBytesUploaded = 0; - try { - timings.start("connecting"); - yield connect(client, args); - timings.stop("connecting"); - try { - const serverFiles = yield getServerFiles(client, logger, timings, args); - const diffTool = new HashDiff_1.HashDiff(); - const diffs = diffTool.getDiffs(localFiles, serverFiles, logger); - totalBytesUploaded = diffs.sizeUpload + diffs.sizeReplace; - timings.start("upload"); - try { - yield syncLocalToServer(client, diffs, logger, timings, args); - } - catch (e) { - if (e.code === types_1.ErrorCode.FileNameNotAllowed) { - logger.warn("Error 553 FileNameNotAllowed, you don't have access to upload that file"); - logger.warn(e); - process.exit(); - } - logger.warn(e); - process.exit(); - } - finally { - timings.stop("upload"); - } - } - catch (error) { - const ftpError = error; - if (ftpError.code === types_1.ErrorCode.FileNotFoundOrNoAccess) { - logger.warn("Couldn't find file"); - } - logger.warn(ftpError); - } - } - catch (error) { - errorHandling_1.prettyError(logger, args, error); - } - finally { - client.close(); - timings.stop("total"); - } - const 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(` - changing dirs: ${timings.getTimeFormatted("changingDir")}`); - logger.all(`----------------------------------------------------------------`); - logger.all(`Total time: ${timings.getTimeFormatted("total")}`); - logger.all(`----------------------------------------------------------------`); - }); -} -exports.deploy = deploy; - - -/***/ }), - -/***/ 6703: -/***/ ((__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 = {})); -; - - -/***/ }), - -/***/ 4389: -/***/ (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 })); -exports.Timer = exports.Timings = exports.pluralize = exports.Logger = void 0; -const pretty_ms_1 = __importDefault(__webpack_require__(1127)); -class Logger { - constructor(level) { - this.level = level !== null && level !== void 0 ? level : "info"; - } - all(...data) { - console.log(...data); - } - warn(...data) { - if (this.level === "debug") { - return; - } - console.log(...data); - } - info(...data) { - if (this.level === "warn") { - return; - } - console.log(...data); - } - debug(...data) { - if (this.level !== "debug") { - return; - } - console.log(...data); - } -} -exports.Logger = Logger; -function pluralize(count, singular, plural) { - if (count === 1) { - return singular; - } - return plural; -} -exports.pluralize = pluralize; -class Timings { - constructor() { - this.timers = {}; - } - start(type) { - if (this.timers[type] === undefined) { - this.timers[type] = new Timer(); - } - this.timers[type].start(); - } - stop(type) { - this.timers[type].stop(); - } - getTime(type) { - const timer = this.timers[type]; - if (timer === undefined || timer.time === null) { - return 0; - } - return timer.time; - } - getTimeFormatted(type) { - const timer = this.timers[type]; - if (timer === undefined || timer.time === null) { - return "๐Ÿ’ฃ Failed"; - } - return pretty_ms_1.default(timer.time, { verbose: true }); - } -} -exports.Timings = Timings; -class Timer { - constructor() { - this.totalTime = null; - this.startTime = null; - this.endTime = null; - } - start() { - this.startTime = process.hrtime(); - } - stop() { - if (this.startTime === null) { - throw new Error("Called .stop() before calling .start()"); - } - this.endTime = process.hrtime(this.startTime); - const currentSeconds = this.totalTime === null ? 0 : this.totalTime[0]; - const currentNS = this.totalTime === null ? 0 : this.totalTime[1]; - this.totalTime = [ - currentSeconds + this.endTime[0], - currentNS + this.endTime[1] - ]; - } - get time() { - if (this.totalTime === null) { - return null; - } - return (this.totalTime[0] * 1000) + (this.totalTime[1] / 1000000); - } -} -exports.Timer = Timer; - - -/***/ }), - -/***/ 6554: -/***/ ((module) => { - -"use strict"; - - -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, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.Client = void 0; -const fs_1 = __webpack_require__(5747); -const path_1 = __webpack_require__(5622); -const tls_1 = __webpack_require__(4016); -const util_1 = __webpack_require__(1669); -const FtpContext_1 = __webpack_require__(9052); -const parseList_1 = __webpack_require__(2993); -const ProgressTracker_1 = __webpack_require__(7170); -const StringWriter_1 = __webpack_require__(8184); -const parseListMLSD_1 = __webpack_require__(8157); -const netUtils_1 = __webpack_require__(6288); -const transfer_1 = __webpack_require__(5803); -const parseControlResponse_1 = __webpack_require__(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); -const fsStat = util_1.promisify(fs_1.stat); -const fsOpen = util_1.promisify(fs_1.open); -const fsClose = util_1.promisify(fs_1.close); -const fsUnlink = util_1.promisify(fs_1.unlink); -/** - * High-level API to interact with an FTP server. - */ -class Client { - /** - * Instantiate an FTP client. - * - * @param timeout Timeout in milliseconds, use 0 for no timeout. Optional, default is 30 seconds. - */ - constructor(timeout = 30000) { - /** - * Multiple commands to retrieve a directory listing are possible. This instance - * will try all of them in the order presented the first time a directory listing - * is requested. After that, `availableListCommands` will hold only the first - * entry that worked. - */ - this.availableListCommands = ["MLSD", "LIST -a", "LIST"]; - this.ftp = new FtpContext_1.FTPContext(timeout); - this.prepareTransfer = this._enterFirstCompatibleMode([transfer_1.enterPassiveModeIPv6, transfer_1.enterPassiveModeIPv4]); - this.parseList = parseList_1.parseList; - this._progressTracker = new ProgressTracker_1.ProgressTracker(); - } - /** - * Close the client and all open socket connections. - * - * Close the client and all open socket connections. The client canโ€™t be used anymore after calling this method, - * you have to either reconnect with `access` or `connect` or instantiate a new instance to continue any work. - * A client is also closed automatically if any timeout or connection error occurs. - */ - close() { - this.ftp.close(); - this._progressTracker.stop(); - } - /** - * Returns true if the client is closed and can't be used anymore. - */ - get closed() { - return this.ftp.closed; - } - /** - * Connect (or reconnect) to an FTP server. - * - * This is an instance method and thus can be called multiple times during the lifecycle of a `Client` - * instance. Whenever you do, the client is reset with a new control connection. This also implies that - * you can reopen a `Client` instance that has been closed due to an error when reconnecting with this - * method. In fact, reconnecting is the only way to continue using a closed `Client`. - * - * @param host Host the client should connect to. Optional, default is "localhost". - * @param port Port the client should connect to. Optional, default is 21. - */ - connect(host = "localhost", port = 21) { - this.ftp.reset(); - this.ftp.socket.connect({ - host, - port, - family: this.ftp.ipFamily - }, () => this.ftp.log(`Connected to ${netUtils_1.describeAddress(this.ftp.socket)} (${netUtils_1.describeTLS(this.ftp.socket)})`)); - return this._handleConnectResponse(); - } - /** - * As `connect` but using implicit TLS. Implicit TLS is not an FTP standard and has been replaced by - * explicit TLS. There are still FTP servers that support only implicit TLS, though. - */ - connectImplicitTLS(host = "localhost", port = 21, tlsOptions = {}) { - this.ftp.reset(); - this.ftp.socket = tls_1.connect(port, host, tlsOptions, () => this.ftp.log(`Connected to ${netUtils_1.describeAddress(this.ftp.socket)} (${netUtils_1.describeTLS(this.ftp.socket)})`)); - this.ftp.tlsOptions = tlsOptions; - return this._handleConnectResponse(); - } - /** - * Handles the first reponse by an FTP server after the socket connection has been established. - */ - _handleConnectResponse() { - return this.ftp.handle(undefined, (res, task) => { - if (res instanceof Error) { - // The connection has been destroyed by the FTPContext at this point. - task.reject(res); - } - else if (parseControlResponse_1.positiveCompletion(res.code)) { - task.resolve(res); - } - // Reject all other codes, including 120 "Service ready in nnn minutes". - else { - // Don't stay connected but don't replace the socket yet by using reset() - // so the user can inspect properties of this instance. - this.ftp.socket.destroy(); - task.reject(new FtpContext_1.FTPError(res)); - } - }); - } - /** - * Send an FTP command and handle the first response. - */ - send(command, ignoreErrorCodesDEPRECATED = false) { - if (ignoreErrorCodesDEPRECATED) { // Deprecated starting from 3.9.0 - this.ftp.log("Deprecated call using send(command, flag) with boolean flag to ignore errors. Use sendIgnoringError(command)."); - return this.sendIgnoringError(command); - } - return this.ftp.request(command); - } - /** - * Send an FTP command and ignore an FTP error response. Any other kind of error or timeout will still reject the Promise. - * - * @param command - */ - sendIgnoringError(command) { - return this.ftp.handle(command, (res, task) => { - if (res instanceof FtpContext_1.FTPError) { - task.resolve({ code: res.code, message: res.message }); - } - else if (res instanceof Error) { - task.reject(res); - } - else { - task.resolve(res); - } - }); - } - /** - * Upgrade the current socket connection to TLS. - * - * @param options TLS options as in `tls.connect(options)`, optional. - * @param command Set the authentication command. Optional, default is "AUTH TLS". - */ - async useTLS(options = {}, command = "AUTH TLS") { - const ret = await this.send(command); - this.ftp.socket = await netUtils_1.upgradeSocket(this.ftp.socket, options); - this.ftp.tlsOptions = options; // Keep the TLS options for later data connections that should use the same options. - this.ftp.log(`Control socket is using: ${netUtils_1.describeTLS(this.ftp.socket)}`); - return ret; - } - /** - * Login a user with a password. - * - * @param user Username to use for login. Optional, default is "anonymous". - * @param password Password to use for login. Optional, default is "guest". - */ - login(user = "anonymous", password = "guest") { - this.ftp.log(`Login security: ${netUtils_1.describeTLS(this.ftp.socket)}`); - return this.ftp.handle("USER " + user, (res, task) => { - if (res instanceof Error) { - task.reject(res); - } - else if (parseControlResponse_1.positiveCompletion(res.code)) { // User logged in proceed OR Command superfluous - task.resolve(res); - } - else if (res.code === 331) { // User name okay, need password - this.ftp.send("PASS " + password); - } - else { // Also report error on 332 (Need account) - task.reject(new FtpContext_1.FTPError(res)); - } - }); - } - /** - * Set the usual default settings. - * - * Settings used: - * * Binary mode (TYPE I) - * * File structure (STRU F) - * * Additional settings for FTPS (PBSZ 0, PROT P) - */ - async useDefaultSettings() { - await this.send("TYPE I"); // Binary mode - await this.sendIgnoringError("STRU F"); // Use file structure - await this.sendIgnoringError("OPTS UTF8 ON"); // Some servers expect UTF-8 to be enabled explicitly - await this.sendIgnoringError("OPTS MLST type;size;modify;unique;unix.mode;unix.owner;unix.group;unix.ownername;unix.groupname;"); // Make sure MLSD listings include all we can parse - if (this.ftp.hasTLS) { - await this.sendIgnoringError("PBSZ 0"); // Set to 0 for TLS - await this.sendIgnoringError("PROT P"); // Protect channel (also for data connections) - } - } - /** - * Convenience method that calls `connect`, `useTLS`, `login` and `useDefaultSettings`. - * - * This is an instance method and thus can be called multiple times during the lifecycle of a `Client` - * instance. Whenever you do, the client is reset with a new control connection. This also implies that - * you can reopen a `Client` instance that has been closed due to an error when reconnecting with this - * method. In fact, reconnecting is the only way to continue using a closed `Client`. - */ - async access(options = {}) { - const useExplicitTLS = options.secure === true; - const useImplicitTLS = options.secure === "implicit"; - let welcome; - if (useImplicitTLS) { - welcome = await this.connectImplicitTLS(options.host, options.port, options.secureOptions); - } - else { - welcome = await this.connect(options.host, options.port); - } - if (useExplicitTLS) { - await this.useTLS(options.secureOptions); - } - await this.login(options.user, options.password); - await this.useDefaultSettings(); - return welcome; - } - /** - * Get the current working directory. - */ - async pwd() { - const res = await this.send("PWD"); - // The directory is part of the return message, for example: - // 257 "/this/that" is current directory. - const parsed = res.message.match(/"(.+)"/); - if (parsed === null || parsed[1] === undefined) { - throw new Error(`Can't parse response to command 'PWD': ${res.message}`); - } - return parsed[1]; - } - /** - * Get a description of supported features. - * - * This sends the FEAT command and parses the result into a Map where keys correspond to available commands - * and values hold further information. Be aware that your FTP servers might not support this - * command in which case this method will not throw an exception but just return an empty Map. - */ - async features() { - const res = await this.sendIgnoringError("FEAT"); - const features = new Map(); - // Not supporting any special features will be reported with a single line. - if (res.code < 400 && parseControlResponse_1.isMultiline(res.message)) { - // The first and last line wrap the multiline response, ignore them. - res.message.split("\n").slice(1, -1).forEach(line => { - // A typical lines looks like: " REST STREAM" or " MDTM". - // Servers might not use an indentation though. - const entry = line.trim().split(" "); - features.set(entry[0], entry[1] || ""); - }); - } - return features; - } - /** - * Set the working directory. - */ - async cd(path) { - const validPath = await this.protectWhitespace(path); - return this.send("CWD " + validPath); - } - /** - * Switch to the parent directory of the working directory. - */ - async cdup() { - return this.send("CDUP"); - } - /** - * Get the last modified time of a file. This is not supported by every FTP server, in which case - * calling this method will throw an exception. - */ - async lastMod(path) { - const validPath = await this.protectWhitespace(path); - const res = await this.send(`MDTM ${validPath}`); - const date = res.message.slice(4); - return parseListMLSD_1.parseMLSxDate(date); - } - /** - * Get the size of a file. - */ - async size(path) { - const validPath = await this.protectWhitespace(path); - const command = `SIZE ${validPath}`; - const res = await this.send(command); - // The size is part of the response message, for example: "213 555555". It's - // possible that there is a commmentary appended like "213 5555, some commentary". - const size = parseInt(res.message.slice(4), 10); - if (Number.isNaN(size)) { - throw new Error(`Can't parse response to command '${command}' as a numerical value: ${res.message}`); - } - return size; - } - /** - * Rename a file. - * - * Depending on the FTP server this might also be used to move a file from one - * directory to another by providing full paths. - */ - async rename(srcPath, destPath) { - const validSrc = await this.protectWhitespace(srcPath); - const validDest = await this.protectWhitespace(destPath); - await this.send("RNFR " + validSrc); - return this.send("RNTO " + validDest); - } - /** - * Remove a file from the current working directory. - * - * You can ignore FTP error return codes which won't throw an exception if e.g. - * the file doesn't exist. - */ - async remove(path, ignoreErrorCodes = false) { - const validPath = await this.protectWhitespace(path); - return this.send(`DELE ${validPath}`, ignoreErrorCodes); - } - /** - * Report transfer progress for any upload or download to a given handler. - * - * This will also reset the overall transfer counter that can be used for multiple transfers. You can - * also call the function without a handler to stop reporting to an earlier one. - * - * @param handler Handler function to call on transfer progress. - */ - trackProgress(handler) { - this._progressTracker.bytesOverall = 0; - this._progressTracker.reportTo(handler); - } - /** - * Upload data from a readable stream or a local file to a remote file. - * - * @param source Readable stream or path to a local file. - * @param toRemotePath Path to a remote file to write to. - */ - async uploadFrom(source, toRemotePath, options = {}) { - return this._uploadWithCommand(source, toRemotePath, "STOR", options); - } - /** - * Upload data from a readable stream or a local file by appending it to an existing file. If the file doesn't - * exist the FTP server should create it. - * - * @param source Readable stream or path to a local file. - * @param toRemotePath Path to a remote file to write to. - */ - async appendFrom(source, toRemotePath, options = {}) { - return this._uploadWithCommand(source, toRemotePath, "APPE", options); - } - /** - * @protected - */ - async _uploadWithCommand(source, remotePath, command, options) { - if (typeof source === "string") { - return this._uploadLocalFile(source, remotePath, command, options); - } - return this._uploadFromStream(source, remotePath, command); - } - /** - * @protected - */ - async _uploadLocalFile(localPath, remotePath, command, options) { - const fd = await fsOpen(localPath, "r"); - const source = fs_1.createReadStream("", { - fd, - start: options.localStart, - end: options.localEndInclusive, - autoClose: false - }); - try { - return await this._uploadFromStream(source, remotePath, command); - } - finally { - await ignoreError(() => fsClose(fd)); - } - } - /** - * @protected - */ - async _uploadFromStream(source, remotePath, command) { - const onError = (err) => this.ftp.closeWithError(err); - source.once("error", onError); - try { - const validPath = await this.protectWhitespace(remotePath); - await this.prepareTransfer(this.ftp); - // Keep the keyword `await` or the `finally` clause below runs too early - // and removes the event listener for the source stream too early. - return await transfer_1.uploadFrom(source, { - ftp: this.ftp, - tracker: this._progressTracker, - command, - remotePath: validPath, - type: "upload" - }); - } - finally { - source.removeListener("error", onError); - } - } - /** - * Download a remote file and pipe its data to a writable stream or to a local file. - * - * You can optionally define at which position of the remote file you'd like to start - * downloading. If the destination you provide is a file, the offset will be applied - * to it as well. For example: To resume a failed download, you'd request the size of - * the local, partially downloaded file and use that as the offset. Assuming the size - * is 23, you'd download the rest using `downloadTo("local.txt", "remote.txt", 23)`. - * - * @param destination Stream or path for a local file to write to. - * @param fromRemotePath Path of the remote file to read from. - * @param startAt Position within the remote file to start downloading at. If the destination is a file, this offset is also applied to it. - */ - async downloadTo(destination, fromRemotePath, startAt = 0) { - if (typeof destination === "string") { - return this._downloadToFile(destination, fromRemotePath, startAt); - } - return this._downloadToStream(destination, fromRemotePath, startAt); - } - /** - * @protected - */ - async _downloadToFile(localPath, remotePath, startAt) { - const appendingToLocalFile = startAt > 0; - const fileSystemFlags = appendingToLocalFile ? "r+" : "w"; - const fd = await fsOpen(localPath, fileSystemFlags); - const destination = fs_1.createWriteStream("", { - fd, - start: startAt, - autoClose: false - }); - try { - return await this._downloadToStream(destination, remotePath, startAt); - } - catch (err) { - const localFileStats = await ignoreError(() => fsStat(localPath)); - const hasDownloadedData = localFileStats && localFileStats.size > 0; - const shouldRemoveLocalFile = !appendingToLocalFile && !hasDownloadedData; - if (shouldRemoveLocalFile) { - await ignoreError(() => fsUnlink(localPath)); - } - throw err; - } - finally { - await ignoreError(() => fsClose(fd)); - } - } - /** - * @protected - */ - async _downloadToStream(destination, remotePath, startAt) { - const onError = (err) => this.ftp.closeWithError(err); - destination.once("error", onError); - try { - const validPath = await this.protectWhitespace(remotePath); - await this.prepareTransfer(this.ftp); - // Keep the keyword `await` or the `finally` clause below runs too early - // and removes the event listener for the source stream too early. - return await transfer_1.downloadTo(destination, { - ftp: this.ftp, - tracker: this._progressTracker, - command: startAt > 0 ? `REST ${startAt}` : `RETR ${validPath}`, - remotePath: validPath, - type: "download" - }); - } - finally { - destination.removeListener("error", onError); - destination.end(); - } - } - /** - * List files and directories in the current working directory, or from `path` if specified. - * - * @param [path] Path to remote file or directory. - */ - async list(path = "") { - const validPath = await this.protectWhitespace(path); - let lastError; - for (const candidate of this.availableListCommands) { - const command = validPath === "" ? candidate : `${candidate} ${validPath}`; - await this.prepareTransfer(this.ftp); - try { - const parsedList = await this._requestListWithCommand(command); - // Use successful candidate for all subsequent requests. - this.availableListCommands = [candidate]; - return parsedList; - } - catch (err) { - const shouldTryNext = err instanceof FtpContext_1.FTPError; - if (!shouldTryNext) { - throw err; - } - lastError = err; - } - } - throw lastError; - } - /** - * @protected - */ - async _requestListWithCommand(command) { - const buffer = new StringWriter_1.StringWriter(); - await transfer_1.downloadTo(buffer, { - ftp: this.ftp, - tracker: this._progressTracker, - command, - remotePath: "", - type: "list" - }); - const text = buffer.getText(this.ftp.encoding); - this.ftp.log(text); - return this.parseList(text); - } - /** - * Remove a directory and all of its content. - * - * @param remoteDirPath The path of the remote directory to delete. - * @example client.removeDir("foo") // Remove directory 'foo' using a relative path. - * @example client.removeDir("foo/bar") // Remove directory 'bar' using a relative path. - * @example client.removeDir("/foo/bar") // Remove directory 'bar' using an absolute path. - * @example client.removeDir("/") // Remove everything. - */ - async removeDir(remoteDirPath) { - return this._exitAtCurrentDirectory(async () => { - await this.cd(remoteDirPath); - await this.clearWorkingDir(); - if (remoteDirPath !== "/") { - await this.cdup(); - await this.removeEmptyDir(remoteDirPath); - } - }); - } - /** - * Remove all files and directories in the working directory without removing - * the working directory itself. - */ - async clearWorkingDir() { - for (const file of await this.list()) { - if (file.isDirectory) { - await this.cd(file.name); - await this.clearWorkingDir(); - await this.cdup(); - await this.removeEmptyDir(file.name); - } - else { - await this.remove(file.name); - } - } - } - /** - * Upload the contents of a local directory to the remote working directory. - * - * This will overwrite existing files with the same names and reuse existing directories. - * Unrelated files and directories will remain untouched. You can optionally provide a `remoteDirPath` - * to put the contents inside a directory which will be created if necessary including all - * intermediate directories. If you did provide a remoteDirPath the working directory will stay - * the same as before calling this method. - * - * @param localDirPath Local path, e.g. "foo/bar" or "../test" - * @param [remoteDirPath] Remote path of a directory to upload to. Working directory if undefined. - */ - async uploadFromDir(localDirPath, remoteDirPath) { - return this._exitAtCurrentDirectory(async () => { - if (remoteDirPath) { - await this.ensureDir(remoteDirPath); - } - return await this._uploadToWorkingDir(localDirPath); - }); - } - /** - * @protected - */ - async _uploadToWorkingDir(localDirPath) { - const files = await fsReadDir(localDirPath); - for (const file of files) { - const fullPath = path_1.join(localDirPath, file); - const stats = await fsStat(fullPath); - if (stats.isFile()) { - await this.uploadFrom(fullPath, file); - } - else if (stats.isDirectory()) { - await this._openDir(file); - await this._uploadToWorkingDir(fullPath); - await this.cdup(); - } - } - } - /** - * Download all files and directories of the working directory to a local directory. - * - * @param localDirPath The local directory to download to. - * @param remoteDirPath Remote directory to download. Current working directory if not specified. - */ - async downloadToDir(localDirPath, remoteDirPath) { - return this._exitAtCurrentDirectory(async () => { - if (remoteDirPath) { - await this.cd(remoteDirPath); - } - return await this._downloadFromWorkingDir(localDirPath); - }); - } - /** - * @protected - */ - async _downloadFromWorkingDir(localDirPath) { - await ensureLocalDirectory(localDirPath); - for (const file of await this.list()) { - const localPath = path_1.join(localDirPath, file.name); - if (file.isDirectory) { - await this.cd(file.name); - await this._downloadFromWorkingDir(localPath); - await this.cdup(); - } - else if (file.isFile) { - await this.downloadTo(localPath, file.name); - } - } - } - /** - * Make sure a given remote path exists, creating all directories as necessary. - * This function also changes the current working directory to the given path. - */ - async ensureDir(remoteDirPath) { - // If the remoteDirPath was absolute go to root directory. - if (remoteDirPath.startsWith("/")) { - await this.cd("/"); - } - const names = remoteDirPath.split("/").filter(name => name !== ""); - for (const name of names) { - await this._openDir(name); - } - } - /** - * Try to create a directory and enter it. This will not raise an exception if the directory - * couldn't be created if for example it already exists. - * @protected - */ - async _openDir(dirName) { - await this.sendIgnoringError("MKD " + dirName); - await this.cd(dirName); - } - /** - * Remove an empty directory, will fail if not empty. - */ - async removeEmptyDir(path) { - const validPath = await this.protectWhitespace(path); - return this.send(`RMD ${validPath}`); - } - /** - * FTP servers can't handle filenames that have leading whitespace. This method transforms - * a given path to fix that issue for most cases. - */ - async protectWhitespace(path) { - if (!path.startsWith(" ")) { - return path; - } - // Handle leading whitespace by prepending the absolute path: - // " test.txt" while being in the root directory becomes "/ test.txt". - const pwd = await this.pwd(); - const absolutePathPrefix = pwd.endsWith("/") ? pwd : pwd + "/"; - return absolutePathPrefix + path; - } - async _exitAtCurrentDirectory(func) { - const userDir = await this.pwd(); - try { - return await func(); - } - finally { - if (!this.closed) { - await ignoreError(() => this.cd(userDir)); - } - } - } - /** - * Try all available transfer strategies and pick the first one that works. Update `client` to - * use the working strategy for all successive transfer requests. - * - * @param strategies - * @returns a function that will try the provided strategies. - */ - _enterFirstCompatibleMode(strategies) { - return async (ftp) => { - ftp.log("Trying to find optimal transfer strategy..."); - for (const strategy of strategies) { - try { - const res = await strategy(ftp); - ftp.log("Optimal transfer strategy found."); - this.prepareTransfer = strategy; // eslint-disable-line require-atomic-updates - return res; - } - catch (err) { - // Receiving an FTPError means that the last transfer strategy failed and we should - // try the next one. Any other exception should stop the evaluation of strategies because - // something else went wrong. - if (!(err instanceof FtpContext_1.FTPError)) { - throw err; - } - } - } - throw new Error("None of the available transfer strategies work."); - }; - } - /** - * DEPRECATED, use `uploadFrom`. - * @deprecated - */ - async upload(source, toRemotePath, options = {}) { - this.ftp.log("Warning: upload() has been deprecated, use uploadFrom()."); - return this.uploadFrom(source, toRemotePath, options); - } - /** - * DEPRECATED, use `appendFrom`. - * @deprecated - */ - async append(source, toRemotePath, options = {}) { - this.ftp.log("Warning: append() has been deprecated, use appendFrom()."); - return this.appendFrom(source, toRemotePath, options); - } - /** - * DEPRECATED, use `downloadTo`. - * @deprecated - */ - async download(destination, fromRemotePath, startAt = 0) { - this.ftp.log("Warning: download() has been deprecated, use downloadTo()."); - return this.downloadTo(destination, fromRemotePath, startAt); - } - /** - * DEPRECATED, use `uploadFromDir`. - * @deprecated - */ - async uploadDir(localDirPath, remoteDirPath) { - this.ftp.log("Warning: uploadDir() has been deprecated, use uploadFromDir()."); - return this.uploadFromDir(localDirPath, remoteDirPath); - } - /** - * DEPRECATED, use `downloadToDir`. - * @deprecated - */ - async downloadDir(localDirPath) { - this.ftp.log("Warning: downloadDir() has been deprecated, use downloadToDir()."); - return this.downloadToDir(localDirPath); - } -} -exports.Client = Client; -async function ensureLocalDirectory(path) { - try { - await fsStat(path); - } - catch (err) { - await fsMkDir(path, { recursive: true }); - } -} -async function ignoreError(func) { - try { - return await func(); - } - catch (err) { - // Ignore - return undefined; - } -} - - -/***/ }), - -/***/ 202: -/***/ ((__unused_webpack_module, 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 -}; - - -/***/ }), - -/***/ 9052: -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.FTPContext = exports.FTPError = void 0; -const net_1 = __webpack_require__(1631); -const parseControlResponse_1 = __webpack_require__(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, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.StringWriter = void 0; -const stream_1 = __webpack_require__(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, __webpack_require__) { - -"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(__webpack_require__(8337), exports); -__exportStar(__webpack_require__(9052), exports); -__exportStar(__webpack_require__(202), exports); -__exportStar(__webpack_require__(2993), exports); -__exportStar(__webpack_require__(4677), exports); -var transfer_1 = __webpack_require__(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, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.ipIsPrivateV4Address = exports.upgradeSocket = exports.describeAddress = exports.describeTLS = void 0; -const tls_1 = __webpack_require__(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, __webpack_require__) { - -"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(__webpack_require__(6199)); -const unixParser = __importStar(__webpack_require__(2622)); -const mlsdParser = __importStar(__webpack_require__(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, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.transformList = exports.parseLine = exports.testLine = void 0; -const FileInfo_1 = __webpack_require__(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, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.parseMLSxDate = exports.transformList = exports.parseLine = exports.testLine = void 0; -const FileInfo_1 = __webpack_require__(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, __webpack_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.transformList = exports.parseLine = exports.testLine = void 0; -const FileInfo_1 = __webpack_require__(202); -const JA_MONTH = "\u6708"; -const JA_DAY = "\u65e5"; -const JA_YEAR = "\u5e74"; -/** - * 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/UnixFTPEntryParser.java - * - * Below is the regular expression used by this parser. - * - * Permissions: - * r the file is readable - * w the file is writable - * x the file is executable - * - the indicated permission is not granted - * L mandatory locking occurs during access (the set-group-ID bit is - * on and the group execution bit is off) - * s the set-user-ID or set-group-ID bit is on, and the corresponding - * user or group execution bit is also on - * S undefined bit-state (the set-user-ID bit is on and the user - * execution bit is off) - * t the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and - * execution is on - * T the 1000 bit is turned on, and execution is off (undefined bit- - * state) - * e z/OS external link bit - * Final letter may be appended: - * + file has extended security attributes (e.g. ACL) - * Note: local listings on MacOSX also use '@' - * this is not allowed for here as does not appear to be shown by FTP servers - * {@code @} file has extended attributes - */ -const RE_LINE = new RegExp("([bcdelfmpSs-])" // file type - + "(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?" // permissions - + "\\s*" // separator TODO why allow it to be omitted?? - + "(\\d+)" // link count - + "\\s+" // separator - + "(?:(\\S+(?:\\s\\S+)*?)\\s+)?" // owner name (optional spaces) - + "(?:(\\S+(?:\\s\\S+)*)\\s+)?" // group name (optional spaces) - + "(\\d+(?:,\\s*\\d+)?)" // size or n,m - + "\\s+" // separator - /** - * numeric or standard format date: - * yyyy-mm-dd (expecting hh:mm to follow) - * MMM [d]d - * [d]d MMM - * N.B. use non-space for MMM to allow for languages such as German which use - * diacritics (e.g. umlaut) in some abbreviations. - * Japanese uses numeric day and month with suffixes to distinguish them - * [d]dXX [d]dZZ - */ - + "(" + - "(?:\\d+[-/]\\d+[-/]\\d+)" + // yyyy-mm-dd - "|(?:\\S{3}\\s+\\d{1,2})" + // MMM [d]d - "|(?:\\d{1,2}\\s+\\S{3})" + // [d]d MMM - "|(?:\\d{1,2}" + JA_MONTH + "\\s+\\d{1,2}" + JA_DAY + ")" + - ")" - + "\\s+" // separator - /** - * year (for non-recent standard format) - yyyy - * or time (for numeric or recent standard format) [h]h:mm - * or Japanese year - yyyyXX - */ - + "((?:\\d+(?::\\d+)?)|(?:\\d{4}" + JA_YEAR + "))" // (20) - + "\\s" // separator - + "(.*)"); // the rest (21) -/** - * Returns true if a given line might be a Unix-style listing. - * - * - Example: `-rw-r--r--+ 1 patrick staff 1057 Dec 11 14:35 test.txt` - */ -function testLine(line) { - return RE_LINE.test(line); -} -exports.testLine = testLine; -/** - * Parse a single line of a Unix-style directory listing. - */ -function parseLine(line) { - const groups = line.match(RE_LINE); - if (groups === null) { - return undefined; - } - const name = groups[21]; - if (name === "." || name === "..") { // Ignore parent directory links - return undefined; - } - const file = new FileInfo_1.FileInfo(name); - file.size = parseInt(groups[18], 10); - file.user = groups[16]; - file.group = groups[17]; - file.hardLinkCount = parseInt(groups[15], 10); - file.rawModifiedAt = groups[19] + " " + groups[20]; - file.permissions = { - user: parseMode(groups[4], groups[5], groups[6]), - group: parseMode(groups[8], groups[9], groups[10]), - world: parseMode(groups[12], groups[13], groups[14]), - }; - // Set file type - switch (groups[1].charAt(0)) { - case "d": - file.type = FileInfo_1.FileType.Directory; - break; - case "e": // NET-39 => z/OS external link - file.type = FileInfo_1.FileType.SymbolicLink; - break; - case "l": - file.type = FileInfo_1.FileType.SymbolicLink; - break; - case "b": - case "c": - file.type = FileInfo_1.FileType.File; // TODO change this if DEVICE_TYPE implemented - break; - case "f": - case "-": - file.type = FileInfo_1.FileType.File; - break; - default: - // A 'whiteout' file is an ARTIFICIAL entry in any of several types of - // 'translucent' filesystems, of which a 'union' filesystem is one. - file.type = FileInfo_1.FileType.Unknown; - } - // Separate out the link name for symbolic links - if (file.isSymbolicLink) { - const end = name.indexOf(" -> "); - if (end !== -1) { - file.name = name.substring(0, end); - file.link = name.substring(end + 4); - } - } - return file; -} -exports.parseLine = parseLine; -function transformList(files) { - return files; -} -exports.transformList = transformList; -function parseMode(r, w, x) { - let value = 0; - if (r !== "-") { - value += FileInfo_1.FileInfo.UnixPermission.Read; - } - if (w !== "-") { - value += FileInfo_1.FileInfo.UnixPermission.Write; - } - const execToken = x.charAt(0); - if (execToken !== "-" && execToken.toUpperCase() !== execToken) { - value += FileInfo_1.FileInfo.UnixPermission.Execute; - } - return value; -} - - -/***/ }), - -/***/ 5803: -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - -"use strict"; - -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 = __webpack_require__(6288); -const tls_1 = __webpack_require__(4016); -const parseControlResponse_1 = __webpack_require__(9948); -/** - * Prepare a data socket using passive mode over IPv6. - */ -async function enterPassiveModeIPv6(ftp) { - const res = await ftp.request("EPSV"); - const port = parseEpsvResponse(res.message); - if (!port) { - throw new Error("Can't parse EPSV response: " + res.message); - } - const controlHost = ftp.socket.remoteAddress; - if (controlHost === undefined) { - throw new Error("Control socket is disconnected, can't get remote address."); - } - await connectForPassiveTransfer(controlHost, port, ftp); - return res; -} -exports.enterPassiveModeIPv6 = enterPassiveModeIPv6; -/** - * Parse an EPSV response. Returns only the port as in EPSV the host of the control connection is used. - */ -function parseEpsvResponse(message) { - // Get port from EPSV response, e.g. "229 Entering Extended Passive Mode (|||6446|)" - // Some FTP Servers such as the one on IBM i (OS/400) use ! instead of | in their EPSV response. - const groups = message.match(/[|!]{3}(.+)[|!]/); - if (groups === null || groups[1] === undefined) { - throw new Error(`Can't parse response to 'EPSV': ${message}`); - } - const port = parseInt(groups[1], 10); - if (Number.isNaN(port)) { - throw new Error(`Can't parse response to 'EPSV', port is not a number: ${message}`); - } - return port; -} -exports.parseEpsvResponse = parseEpsvResponse; -/** - * Prepare a data socket using passive mode over IPv4. - */ -async function enterPassiveModeIPv4(ftp) { - const res = await ftp.request("PASV"); - const target = parsePasvResponse(res.message); - if (!target) { - throw new Error("Can't parse PASV response: " + res.message); - } - // If the host in the PASV response has a local address while the control connection hasn't, - // we assume a NAT issue and use the IP of the control connection as the target for the data connection. - // We can't always perform this replacement because it's possible (although unlikely) that the FTP server - // indeed uses a different host for data connections. - const controlHost = ftp.socket.remoteAddress; - if (netUtils_1.ipIsPrivateV4Address(target.host) && controlHost && !netUtils_1.ipIsPrivateV4Address(controlHost)) { - target.host = controlHost; - } - await connectForPassiveTransfer(target.host, target.port, ftp); - return res; -} -exports.enterPassiveModeIPv4 = enterPassiveModeIPv4; -/** - * Parse a PASV response. - */ -function parsePasvResponse(message) { - // Get host and port from PASV response, e.g. "227 Entering Passive Mode (192,168,1,100,10,229)" - const groups = message.match(/([-\d]+,[-\d]+,[-\d]+,[-\d]+),([-\d]+),([-\d]+)/); - if (groups === null || groups.length !== 4) { - throw new Error(`Can't parse response to 'PASV': ${message}`); - } - return { - host: groups[1].replace(/,/g, "."), - port: (parseInt(groups[2], 10) & 255) * 256 + (parseInt(groups[3], 10) & 255) - }; -} -exports.parsePasvResponse = parsePasvResponse; -function connectForPassiveTransfer(host, port, ftp) { - return new Promise((resolve, reject) => { - const handleConnErr = function (err) { - err.message = "Can't open data connection in passive mode: " + err.message; - reject(err); - }; - let socket = ftp._newSocket(); - socket.on("error", handleConnErr); - socket.connect({ port, host, family: ftp.ipFamily }, () => { - if (ftp.socket instanceof tls_1.TLSSocket) { - socket = tls_1.connect(Object.assign({}, ftp.tlsOptions, { - socket, - // Reuse the TLS session negotiated earlier when the control connection - // was upgraded. Servers expect this because it provides additional - // security: If a completely new session would be negotiated, a hacker - // could guess the port and connect to the new data connection before we do - // by just starting his/her own TLS session. - session: ftp.socket.getSession() - })); - // It's the responsibility of the transfer task to wait until the - // TLS socket issued the event 'secureConnect'. We can't do this - // here because some servers will start upgrading after the - // specific transfer request has been made. List and download don't - // have to wait for this event because the server sends whenever it - // is ready. But for upload this has to be taken into account, - // see the details in the upload() function below. - } - // Let the FTPContext listen to errors from now on, remove local handler. - socket.removeListener("error", handleConnErr); - ftp.dataSocket = socket; - resolve(); - }); - }); -} -exports.connectForPassiveTransfer = connectForPassiveTransfer; -/** - * Helps resolving/rejecting transfers. - * - * This is used internally for all FTP transfers. For example when downloading, the server might confirm - * with "226 Transfer complete" when in fact the download on the data connection has not finished - * yet. With all transfers we make sure that a) the result arrived and b) has been confirmed by - * e.g. the control connection. We just don't know in which order this will happen. - */ -class TransferResolver { - /** - * Instantiate a TransferResolver - */ - constructor(ftp, progress) { - this.ftp = ftp; - this.progress = progress; - this.response = undefined; - this.dataTransferDone = false; - } - /** - * Mark the beginning of a transfer. - * - * @param name - Name of the transfer, usually the filename. - * @param type - Type of transfer, usually "upload" or "download". - */ - onDataStart(name, type) { - // Let the data socket be in charge of tracking timeouts during transfer. - // The control socket sits idle during this time anyway and might provoke - // a timeout unnecessarily. The control connection will take care - // of timeouts again once data transfer is complete or failed. - if (this.ftp.dataSocket === undefined) { - throw new Error("Data transfer should start but there is no data connection."); - } - this.ftp.socket.setTimeout(0); - this.ftp.dataSocket.setTimeout(this.ftp.timeout); - this.progress.start(this.ftp.dataSocket, name, type); - } - /** - * The data connection has finished the transfer. - */ - onDataDone(task) { - this.progress.updateAndStop(); - // Hand-over timeout tracking back to the control connection. It's possible that - // we don't receive the response over the control connection that the transfer is - // done. In this case, we want to correctly associate the resulting timeout with - // the control connection. - this.ftp.socket.setTimeout(this.ftp.timeout); - if (this.ftp.dataSocket) { - this.ftp.dataSocket.setTimeout(0); - } - this.dataTransferDone = true; - this.tryResolve(task); - } - /** - * The control connection reports the transfer as finished. - */ - onControlDone(task, response) { - this.response = response; - this.tryResolve(task); - } - /** - * An error has been reported and the task should be rejected. - */ - onError(task, err) { - this.progress.updateAndStop(); - this.ftp.socket.setTimeout(this.ftp.timeout); - this.ftp.dataSocket = undefined; - task.reject(err); - } - /** - * Control connection sent an unexpected request requiring a response from our part. We - * can't provide that (because unknown) and have to close the contrext with an error because - * the FTP server is now caught up in a state we can't resolve. - */ - onUnexpectedRequest(response) { - const err = new Error(`Unexpected FTP response is requesting an answer: ${response.message}`); - this.ftp.closeWithError(err); - } - tryResolve(task) { - // To resolve, we need both control and data connection to report that the transfer is done. - const canResolve = this.dataTransferDone && this.response !== undefined; - if (canResolve) { - this.ftp.dataSocket = undefined; - task.resolve(this.response); - } - } -} -function uploadFrom(source, config) { - const resolver = new TransferResolver(config.ftp, config.tracker); - const fullCommand = `${config.command} ${config.remotePath}`; - return config.ftp.handle(fullCommand, (res, task) => { - if (res instanceof Error) { - resolver.onError(task, res); - } - else if (res.code === 150 || res.code === 125) { // Ready to upload - const dataSocket = config.ftp.dataSocket; - if (!dataSocket) { - resolver.onError(task, new Error("Upload should begin but no data connection is available.")); - return; - } - // If we are using TLS, we have to wait until the dataSocket issued - // 'secureConnect'. If this hasn't happened yet, getCipher() returns undefined. - const canUpload = "getCipher" in dataSocket ? dataSocket.getCipher() !== undefined : true; - onConditionOrEvent(canUpload, dataSocket, "secureConnect", () => { - config.ftp.log(`Uploading to ${netUtils_1.describeAddress(dataSocket)} (${netUtils_1.describeTLS(dataSocket)})`); - resolver.onDataStart(config.remotePath, config.type); - source.pipe(dataSocket).once("finish", () => { - dataSocket.destroy(); // Explicitly close/destroy the socket to signal the end. - resolver.onDataDone(task); - }); - }); - } - else if (parseControlResponse_1.positiveCompletion(res.code)) { // Transfer complete - resolver.onControlDone(task, res); - } - else if (parseControlResponse_1.positiveIntermediate(res.code)) { - resolver.onUnexpectedRequest(res); - } - // Ignore all other positive preliminary response codes (< 200) - }); -} -exports.uploadFrom = uploadFrom; -function downloadTo(destination, config) { - if (!config.ftp.dataSocket) { - throw new Error("Download will be initiated but no data connection is available."); - } - // It's possible that data transmission begins before the control socket - // receives the announcement. Start listening for data immediately. - config.ftp.dataSocket.pipe(destination); - const resolver = new TransferResolver(config.ftp, config.tracker); - return config.ftp.handle(config.command, (res, task) => { - if (res instanceof Error) { - resolver.onError(task, res); - } - else if (res.code === 150 || res.code === 125) { // Ready to download - const dataSocket = config.ftp.dataSocket; - if (!dataSocket) { - resolver.onError(task, new Error("Download should begin but no data connection is available.")); - return; - } - config.ftp.log(`Downloading from ${netUtils_1.describeAddress(dataSocket)} (${netUtils_1.describeTLS(dataSocket)})`); - resolver.onDataStart(config.remotePath, config.type); - onConditionOrEvent(isWritableFinished(destination), destination, "finish", () => resolver.onDataDone(task)); - } - else if (res.code === 350) { // Restarting at startAt. - config.ftp.send("RETR " + config.remotePath); - } - else if (parseControlResponse_1.positiveCompletion(res.code)) { // Transfer complete - resolver.onControlDone(task, res); - } - else if (parseControlResponse_1.positiveIntermediate(res.code)) { - resolver.onUnexpectedRequest(res); - } - // Ignore all other positive preliminary response codes (< 200) - }); -} -exports.downloadTo = downloadTo; -/** - * Calls a function immediately if a condition is met or subscribes to an event and calls - * it once the event is emitted. - * - * @param condition The condition to test. - * @param emitter The emitter to use if the condition is not met. - * @param eventName The event to subscribe to if the condition is not met. - * @param action The function to call. - */ -function onConditionOrEvent(condition, emitter, eventName, action) { - if (condition === true) { - action(); - } - else { - emitter.once(eventName, () => action()); - } -} -/** - * Detect whether a writable stream is finished, supporting Node 8. - * From https://github.com/nodejs/node/blob/3e2a3007107b7a100794f4e4adbde19263fc7464/lib/internal/streams/end-of-stream.js#L28-L33 - */ -function isWritableFinished(stream) { - if (stream.writableFinished) - return true; - const wState = stream._writableState; - if (!wState || wState.errored) - return false; - return wState.finished || (wState.ended && wState.length === 0); -} - - -/***/ }), - -/***/ 3717: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var concatMap = __webpack_require__(6891); -var balanced = __webpack_require__(9417); - -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; -} - - - -/***/ }), - -/***/ 6891: -/***/ ((module) => { - -module.exports = function (xs, fn) { - var res = []; - for (var i = 0; i < xs.length; i++) { - var x = fn(xs[i], i); - if (isArray(x)) res.push.apply(res, x); - else res.push(x); - } - return res; -}; - -var isArray = Array.isArray || function (xs) { - return Object.prototype.toString.call(xs) === '[object Array]'; -}; - - -/***/ }), - -/***/ 7117: -/***/ ((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); -}; - - -/***/ }), - -/***/ 3973: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -module.exports = minimatch -minimatch.Minimatch = Minimatch - -var path = { sep: '/' } -try { - path = __webpack_require__(5622) -} catch (er) {} - -var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {} -var expand = __webpack_require__(3717) - -var plTypes = { - '!': { open: '(?:(?!(?:', close: '))[^/]*?)'}, - '?': { open: '(?:', close: ')?' }, - '+': { open: '(?:', close: ')+' }, - '*': { open: '(?:', close: ')*' }, - '@': { open: '(?:', close: ')' } -} - -// any single thing other than / -// don't need to escape / when using new RegExp() -var qmark = '[^/]' - -// * => any number of characters -var star = qmark + '*?' - -// ** when dots are allowed. Anything goes, except .. and . -// not (^ or / followed by one or two dots followed by $ or /), -// followed by anything, any number of times. -var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?' - -// not a ^ or / followed by a dot, -// followed by anything, any number of times. -var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?' - -// characters that need to be escaped in RegExp. -var reSpecials = charSet('().*{}+?[]^$\\!') - -// "abc" -> { a:true, b:true, c:true } -function charSet (s) { - return s.split('').reduce(function (set, c) { - set[c] = true - return set - }, {}) -} - -// normalizes slashes. -var slashSplit = /\/+/ - -minimatch.filter = filter -function filter (pattern, options) { - options = options || {} - return function (p, i, list) { - return minimatch(p, pattern, options) - } -} - -function ext (a, b) { - a = a || {} - b = b || {} - var t = {} - Object.keys(b).forEach(function (k) { - t[k] = b[k] - }) - Object.keys(a).forEach(function (k) { - t[k] = a[k] - }) - return t -} - -minimatch.defaults = function (def) { - if (!def || !Object.keys(def).length) return minimatch - - var orig = minimatch - - var m = function minimatch (p, pattern, options) { - return orig.minimatch(p, pattern, ext(def, options)) - } - - m.Minimatch = function Minimatch (pattern, options) { - return new orig.Minimatch(pattern, ext(def, options)) - } - - return m -} - -Minimatch.defaults = function (def) { - if (!def || !Object.keys(def).length) return Minimatch - return minimatch.defaults(def).Minimatch -} - -function minimatch (p, pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('glob pattern string required') - } - - if (!options) options = {} - - // shortcut: comments match nothing. - if (!options.nocomment && pattern.charAt(0) === '#') { - return false - } - - // "" only matches "" - if (pattern.trim() === '') return p === '' - - return new Minimatch(pattern, options).match(p) -} - -function Minimatch (pattern, options) { - if (!(this instanceof Minimatch)) { - return new Minimatch(pattern, options) - } - - if (typeof pattern !== 'string') { - throw new TypeError('glob pattern string required') - } - - if (!options) options = {} - pattern = pattern.trim() - - // windows support: need to use /, not \ - if (path.sep !== '/') { - pattern = pattern.split(path.sep).join('/') - } - - this.options = options - this.set = [] - this.pattern = pattern - this.regexp = null - this.negate = false - this.comment = false - this.empty = false - - // make the set of regexps etc. - this.make() -} - -Minimatch.prototype.debug = function () {} - -Minimatch.prototype.make = make -function make () { - // don't do it more than once. - if (this._made) return - - var pattern = this.pattern - var options = this.options - - // empty patterns and comments match nothing. - if (!options.nocomment && pattern.charAt(0) === '#') { - this.comment = true - return - } - if (!pattern) { - this.empty = true - return - } - - // step 1: figure out negation, etc. - this.parseNegate() - - // step 2: expand braces - var set = this.globSet = this.braceExpand() - - if (options.debug) this.debug = console.error - - this.debug(this.pattern, set) - - // step 3: now we have a set, so turn each one into a series of path-portion - // matching patterns. - // These will be regexps, except in the case of "**", which is - // set to the GLOBSTAR object for globstar behavior, - // and will not contain any / characters - set = this.globParts = set.map(function (s) { - return s.split(slashSplit) - }) - - this.debug(this.pattern, set) - - // glob --> regexps - set = set.map(function (s, si, set) { - return s.map(this.parse, this) - }, this) - - this.debug(this.pattern, set) - - // filter out everything that didn't compile properly. - set = set.filter(function (s) { - return s.indexOf(false) === -1 - }) - - this.debug(this.pattern, set) - - this.set = set -} - -Minimatch.prototype.parseNegate = parseNegate -function parseNegate () { - var pattern = this.pattern - var negate = false - var options = this.options - var negateOffset = 0 - - if (options.nonegate) return - - for (var i = 0, l = pattern.length - ; i < l && pattern.charAt(i) === '!' - ; i++) { - negate = !negate - negateOffset++ - } - - if (negateOffset) this.pattern = pattern.substr(negateOffset) - this.negate = negate -} - -// Brace expansion: -// a{b,c}d -> abd acd -// a{b,}c -> abc ac -// a{0..3}d -> a0d a1d a2d a3d -// a{b,c{d,e}f}g -> abg acdfg acefg -// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg -// -// Invalid sets are not expanded. -// a{2..}b -> a{2..}b -// a{b}c -> a{b}c -minimatch.braceExpand = function (pattern, options) { - return braceExpand(pattern, options) -} - -Minimatch.prototype.braceExpand = braceExpand - -function braceExpand (pattern, options) { - if (!options) { - if (this instanceof Minimatch) { - options = this.options - } else { - options = {} - } - } - - pattern = typeof pattern === 'undefined' - ? this.pattern : pattern - - if (typeof pattern === 'undefined') { - throw new TypeError('undefined pattern') - } - - if (options.nobrace || - !pattern.match(/\{.*\}/)) { - // shortcut. no need to expand. - return [pattern] - } - - return expand(pattern) -} - -// parse a component of the expanded set. -// At this point, no pattern may contain "/" in it -// so we're going to return a 2d array, where each entry is the full -// pattern, split on '/', and then turned into a regular expression. -// A regexp is made at the end which joins each array with an -// escaped /, and another full one which joins each regexp with |. -// -// Following the lead of Bash 4.1, note that "**" only has special meaning -// when it is the *only* thing in a path portion. Otherwise, any series -// of * is equivalent to a single *. Globstar behavior is enabled by -// default, and can be disabled by setting options.noglobstar. -Minimatch.prototype.parse = parse -var SUBPARSE = {} -function parse (pattern, isSub) { - if (pattern.length > 1024 * 64) { - throw new TypeError('pattern is too long') - } - - var options = this.options - - // shortcuts - if (!options.noglobstar && pattern === '**') return GLOBSTAR - if (pattern === '') return '' - - var re = '' - var hasMagic = !!options.nocase - var escaping = false - // ? => one single character - var patternListStack = [] - var negativeLists = [] - var stateChar - var inClass = false - var reClassStart = -1 - var classStart = -1 - // . and .. never match anything that doesn't start with ., - // even when options.dot is set. - var patternStart = pattern.charAt(0) === '.' ? '' // anything - // not (start or / followed by . or .. followed by / or end) - : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))' - : '(?!\\.)' - var self = this - - function clearStateChar () { - if (stateChar) { - // we had some state-tracking character - // that wasn't consumed by this pass. - switch (stateChar) { - case '*': - re += star - hasMagic = true - break - case '?': - re += qmark - hasMagic = true - break - default: - re += '\\' + stateChar - break - } - self.debug('clearStateChar %j %j', stateChar, re) - stateChar = false - } - } - - for (var i = 0, len = pattern.length, c - ; (i < len) && (c = pattern.charAt(i)) - ; i++) { - this.debug('%s\t%s %s %j', pattern, i, re, c) - - // skip over any that are escaped. - if (escaping && reSpecials[c]) { - re += '\\' + c - escaping = false - continue - } - - switch (c) { - case '/': - // completely not allowed, even escaped. - // Should already be path-split by now. - return false - - case '\\': - clearStateChar() - escaping = true - continue - - // the various stateChar values - // for the "extglob" stuff. - case '?': - case '*': - case '+': - case '@': - case '!': - this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c) - - // all of those are literals inside a class, except that - // the glob [!a] means [^a] in regexp - if (inClass) { - this.debug(' in class') - if (c === '!' && i === classStart + 1) c = '^' - re += c - continue - } - - // if we already have a stateChar, then it means - // that there was something like ** or +? in there. - // Handle the stateChar, then proceed with this one. - self.debug('call clearStateChar %j', stateChar) - clearStateChar() - stateChar = c - // if extglob is disabled, then +(asdf|foo) isn't a thing. - // just clear the statechar *now*, rather than even diving into - // the patternList stuff. - if (options.noext) clearStateChar() - continue - - case '(': - if (inClass) { - re += '(' - continue - } - - if (!stateChar) { - re += '\\(' - continue - } - - patternListStack.push({ - type: stateChar, - start: i - 1, - reStart: re.length, - open: plTypes[stateChar].open, - close: plTypes[stateChar].close - }) - // negation is (?:(?!js)[^/]*) - re += stateChar === '!' ? '(?:(?!(?:' : '(?:' - this.debug('plType %j %j', stateChar, re) - stateChar = false - continue - - case ')': - if (inClass || !patternListStack.length) { - re += '\\)' - continue - } - - clearStateChar() - hasMagic = true - var pl = patternListStack.pop() - // negation is (?:(?!js)[^/]*) - // The others are (?:) - re += pl.close - if (pl.type === '!') { - negativeLists.push(pl) - } - pl.reEnd = re.length - continue - - case '|': - if (inClass || !patternListStack.length || escaping) { - re += '\\|' - escaping = false - continue - } - - clearStateChar() - re += '|' - continue - - // these are mostly the same in regexp and glob - case '[': - // swallow any state-tracking char before the [ - clearStateChar() - - if (inClass) { - re += '\\' + c - continue - } - - inClass = true - classStart = i - reClassStart = re.length - re += c - continue - - case ']': - // a right bracket shall lose its special - // meaning and represent itself in - // a bracket expression if it occurs - // first in the list. -- POSIX.2 2.8.3.2 - if (i === classStart + 1 || !inClass) { - re += '\\' + c - escaping = false - continue - } - - // handle the case where we left a class open. - // "[z-a]" is valid, equivalent to "\[z-a\]" - if (inClass) { - // split where the last [ was, make sure we don't have - // an invalid re. if so, re-walk the contents of the - // would-be class to re-translate any characters that - // were passed through as-is - // TODO: It would probably be faster to determine this - // without a try/catch and a new RegExp, but it's tricky - // to do safely. For now, this is safe and works. - var cs = pattern.substring(classStart + 1, i) - try { - RegExp('[' + cs + ']') - } catch (er) { - // not a valid class! - var sp = this.parse(cs, SUBPARSE) - re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]' - hasMagic = hasMagic || sp[1] - inClass = false - continue - } - } - - // finish up the class. - hasMagic = true - inClass = false - re += c - continue - - default: - // swallow any state char that wasn't consumed - clearStateChar() - - if (escaping) { - // no need - escaping = false - } else if (reSpecials[c] - && !(c === '^' && inClass)) { - re += '\\' - } - - re += c - - } // switch - } // for - - // handle the case where we left a class open. - // "[abc" is valid, equivalent to "\[abc" - if (inClass) { - // split where the last [ was, and escape it - // this is a huge pita. We now have to re-walk - // the contents of the would-be class to re-translate - // any characters that were passed through as-is - cs = pattern.substr(classStart + 1) - sp = this.parse(cs, SUBPARSE) - re = re.substr(0, reClassStart) + '\\[' + sp[0] - hasMagic = hasMagic || sp[1] - } - - // handle the case where we had a +( thing at the *end* - // of the pattern. - // each pattern list stack adds 3 chars, and we need to go through - // and escape any | chars that were passed through as-is for the regexp. - // Go through and escape them, taking care not to double-escape any - // | chars that were already escaped. - for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) { - var tail = re.slice(pl.reStart + pl.open.length) - this.debug('setting tail', re, pl) - // maybe some even number of \, then maybe 1 \, followed by a | - tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, function (_, $1, $2) { - if (!$2) { - // the | isn't already escaped, so escape it. - $2 = '\\' - } - - // need to escape all those slashes *again*, without escaping the - // one that we need for escaping the | character. As it works out, - // escaping an even number of slashes can be done by simply repeating - // it exactly after itself. That's why this trick works. - // - // I am sorry that you have to see this. - return $1 + $1 + $2 + '|' - }) - - this.debug('tail=%j\n %s', tail, tail, pl, re) - var t = pl.type === '*' ? star - : pl.type === '?' ? qmark - : '\\' + pl.type - - hasMagic = true - re = re.slice(0, pl.reStart) + t + '\\(' + tail - } - - // handle trailing things that only matter at the very end. - clearStateChar() - if (escaping) { - // trailing \\ - re += '\\\\' - } - - // only need to apply the nodot start if the re starts with - // something that could conceivably capture a dot - var addPatternStart = false - switch (re.charAt(0)) { - case '.': - case '[': - case '(': addPatternStart = true - } - - // Hack to work around lack of negative lookbehind in JS - // A pattern like: *.!(x).!(y|z) needs to ensure that a name - // like 'a.xyz.yz' doesn't match. So, the first negative - // lookahead, has to look ALL the way ahead, to the end of - // the pattern. - for (var n = negativeLists.length - 1; n > -1; n--) { - var nl = negativeLists[n] - - var nlBefore = re.slice(0, nl.reStart) - var nlFirst = re.slice(nl.reStart, nl.reEnd - 8) - var nlLast = re.slice(nl.reEnd - 8, nl.reEnd) - var nlAfter = re.slice(nl.reEnd) - - nlLast += nlAfter - - // Handle nested stuff like *(*.js|!(*.json)), where open parens - // mean that we should *not* include the ) in the bit that is considered - // "after" the negated section. - var openParensBefore = nlBefore.split('(').length - 1 - var cleanAfter = nlAfter - for (i = 0; i < openParensBefore; i++) { - cleanAfter = cleanAfter.replace(/\)[+*?]?/, '') - } - nlAfter = cleanAfter - - var dollar = '' - if (nlAfter === '' && isSub !== SUBPARSE) { - dollar = '$' - } - var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast - re = newRe - } - - // if the re is not "" at this point, then we need to make sure - // it doesn't match against an empty path part. - // Otherwise a/* will match a/, which it should not. - if (re !== '' && hasMagic) { - re = '(?=.)' + re - } - - if (addPatternStart) { - re = patternStart + re - } - - // parsing just a piece of a larger pattern. - if (isSub === SUBPARSE) { - return [re, hasMagic] - } - - // skip the regexp for non-magical patterns - // unescape anything in it, though, so that it'll be - // an exact match against a file etc. - if (!hasMagic) { - return globUnescape(pattern) - } - - var flags = options.nocase ? 'i' : '' - try { - var regExp = new RegExp('^' + re + '$', flags) - } catch (er) { - // If it was an invalid regular expression, then it can't match - // anything. This trick looks for a character after the end of - // the string, which is of course impossible, except in multi-line - // mode, but it's not a /m regex. - return new RegExp('$.') - } - - regExp._glob = pattern - regExp._src = re - - return regExp -} - -minimatch.makeRe = function (pattern, options) { - return new Minimatch(pattern, options || {}).makeRe() -} - -Minimatch.prototype.makeRe = makeRe -function makeRe () { - if (this.regexp || this.regexp === false) return this.regexp - - // at this point, this.set is a 2d array of partial - // pattern strings, or "**". - // - // It's better to use .match(). This function shouldn't - // be used, really, but it's pretty convenient sometimes, - // when you just want to work with a regex. - var set = this.set - - if (!set.length) { - this.regexp = false - return this.regexp - } - var options = this.options - - var twoStar = options.noglobstar ? star - : options.dot ? twoStarDot - : twoStarNoDot - var flags = options.nocase ? 'i' : '' - - var re = set.map(function (pattern) { - return pattern.map(function (p) { - return (p === GLOBSTAR) ? twoStar - : (typeof p === 'string') ? regExpEscape(p) - : p._src - }).join('\\\/') - }).join('|') - - // must match entire pattern - // ending in a * or ** will make it less strict. - re = '^(?:' + re + ')$' - - // can match anything, as long as it's not this. - if (this.negate) re = '^(?!' + re + ').*$' - - try { - this.regexp = new RegExp(re, flags) - } catch (ex) { - this.regexp = false - } - return this.regexp -} - -minimatch.match = function (list, pattern, options) { - options = options || {} - var mm = new Minimatch(pattern, options) - list = list.filter(function (f) { - return mm.match(f) - }) - if (mm.options.nonull && !list.length) { - list.push(pattern) - } - return list -} - -Minimatch.prototype.match = match -function match (f, partial) { - this.debug('match', f, this.pattern) - // short-circuit in the case of busted things. - // comments, etc. - if (this.comment) return false - if (this.empty) return f === '' - - if (f === '/' && partial) return true - - var options = this.options - - // windows: need to use /, not \ - if (path.sep !== '/') { - f = f.split(path.sep).join('/') - } - - // treat the test path as a set of pathparts. - f = f.split(slashSplit) - this.debug(this.pattern, 'split', f) - - // just ONE of the pattern sets in this.set needs to match - // in order for it to be valid. If negating, then just one - // match means that we have failed. - // Either way, return on the first hit. - - var set = this.set - this.debug(this.pattern, 'set', set) - - // Find the basename of the path by looking for the last non-empty segment - var filename - var i - for (i = f.length - 1; i >= 0; i--) { - filename = f[i] - if (filename) break - } - - for (i = 0; i < set.length; i++) { - var pattern = set[i] - var file = f - if (options.matchBase && pattern.length === 1) { - file = [filename] - } - var hit = this.matchOne(file, pattern, partial) - if (hit) { - if (options.flipNegate) return true - return !this.negate - } - } - - // didn't get any hits. this is success if it's a negative - // pattern, failure otherwise. - if (options.flipNegate) return false - return this.negate -} - -// set partial to true to test if, for example, -// "/a/b" matches the start of "/*/b/*/d" -// Partial means, if you run out of file before you run -// out of pattern, then that's fine, as long as all -// the parts match. -Minimatch.prototype.matchOne = function (file, pattern, partial) { - var options = this.options - - this.debug('matchOne', - { 'this': this, file: file, pattern: pattern }) - - this.debug('matchOne', file.length, pattern.length) - - for (var fi = 0, - pi = 0, - fl = file.length, - pl = pattern.length - ; (fi < fl) && (pi < pl) - ; fi++, pi++) { - this.debug('matchOne loop') - var p = pattern[pi] - var f = file[fi] - - this.debug(pattern, p, f) - - // should be impossible. - // some invalid regexp stuff in the set. - if (p === false) return false - - if (p === GLOBSTAR) { - this.debug('GLOBSTAR', [pattern, p, f]) - - // "**" - // a/**/b/**/c would match the following: - // a/b/x/y/z/c - // a/x/y/z/b/c - // a/b/x/b/x/c - // a/b/c - // To do this, take the rest of the pattern after - // the **, and see if it would match the file remainder. - // If so, return success. - // If not, the ** "swallows" a segment, and try again. - // This is recursively awful. - // - // a/**/b/**/c matching a/b/x/y/z/c - // - a matches a - // - doublestar - // - matchOne(b/x/y/z/c, b/**/c) - // - b matches b - // - doublestar - // - matchOne(x/y/z/c, c) -> no - // - matchOne(y/z/c, c) -> no - // - matchOne(z/c, c) -> no - // - matchOne(c, c) yes, hit - var fr = fi - var pr = pi + 1 - if (pr === pl) { - this.debug('** at the end') - // a ** at the end will just swallow the rest. - // We have found a match. - // however, it will not swallow /.x, unless - // options.dot is set. - // . and .. are *never* matched by **, for explosively - // exponential reasons. - for (; fi < fl; fi++) { - if (file[fi] === '.' || file[fi] === '..' || - (!options.dot && file[fi].charAt(0) === '.')) return false - } - return true - } - - // ok, let's see if we can swallow whatever we can. - while (fr < fl) { - var swallowee = file[fr] - - this.debug('\nglobstar while', file, fr, pattern, pr, swallowee) - - // XXX remove this slice. Just pass the start index. - if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { - this.debug('globstar found match!', fr, fl, swallowee) - // found a match. - return true - } else { - // can't swallow "." or ".." ever. - // can only swallow ".foo" when explicitly asked. - if (swallowee === '.' || swallowee === '..' || - (!options.dot && swallowee.charAt(0) === '.')) { - this.debug('dot detected!', file, fr, pattern, pr) - break - } - - // ** swallows a segment, and continue. - this.debug('globstar swallow a segment, and continue') - fr++ - } - } - - // no match was found. - // However, in partial mode, we can't say this is necessarily over. - // If there's more *pattern* left, then - if (partial) { - // ran out of file - this.debug('\n>>> no match, partial?', file, fr, pattern, pr) - if (fr === fl) return true - } - return false - } - - // something other than ** - // non-magic patterns just have to match exactly - // patterns with magic have been turned into regexps. - var hit - if (typeof p === 'string') { - if (options.nocase) { - hit = f.toLowerCase() === p.toLowerCase() - } else { - hit = f === p - } - this.debug('string match', p, f, hit) - } else { - hit = f.match(p) - this.debug('pattern match', p, f, hit) - } - - if (!hit) return false - } - - // Note: ending in / means that we'll get a final "" - // at the end of the pattern. This can only match a - // corresponding "" at the end of the file. - // If the file ends in /, then it can only match a - // a pattern that ends in /, unless the pattern just - // doesn't have any more for it. But, a/b/ should *not* - // match "a/b/*", even though "" matches against the - // [^/]*? pattern, except in partial mode, where it might - // simply not be reached yet. - // However, a/b/ should still satisfy a/* - - // now either we fell off the end of the pattern, or we're done. - if (fi === fl && pi === pl) { - // ran out of pattern and filename at the same time. - // an exact hit! - return true - } else if (fi === fl) { - // ran out of file, but still had pattern left. - // this is ok if we're doing the match as part of - // a glob fs traversal. - return partial - } else if (pi === pl) { - // ran out of pattern, still have file left. - // this is only acceptable if we're on the very last - // empty segment of a file with a trailing slash. - // a/* should match a/b/ - var emptyFileEnd = (fi === fl - 1) && (file[fi] === '') - return emptyFileEnd - } - - // should be unreachable. - throw new Error('wtf?') -} - -// replace stuff like \* with * -function globUnescape (s) { - return s.replace(/\\(.)/g, '$1') -} - -function regExpEscape (s) { - return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') -} - - -/***/ }), - -/***/ 4865: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -"use strict"; - -const minimatch = __webpack_require__(3973); -const arrayUnion = __webpack_require__(9600); -const arrayDiffer = __webpack_require__(6554); -const arrify = __webpack_require__(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)); - }, []); -}; - - -/***/ }), - -/***/ 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 BIBYTE_UNITS = [ - 'B', - 'kiB', - 'MiB', - 'GiB', - 'TiB', - 'PiB', - 'EiB', - 'ZiB', - 'YiB' -]; - -const BIT_UNITS = [ - 'b', - 'kbit', - 'Mbit', - 'Gbit', - 'Tbit', - 'Pbit', - 'Ebit', - 'Zbit', - 'Ybit' -]; - -const BIBIT_UNITS = [ - 'b', - 'kibit', - 'Mibit', - 'Gibit', - 'Tibit', - 'Pibit', - 'Eibit', - 'Zibit', - 'Yibit' -]; - -/* -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, binary: false}, options); - const UNITS = options.bits ? - (options.binary ? BIBIT_UNITS : BIT_UNITS) : - (options.binary ? BIBYTE_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(options.binary ? Math.log(number) / Math.log(1024) : Math.log10(number) / 3), UNITS.length - 1); - // eslint-disable-next-line unicorn/prefer-exponentiation-operator - number = Number((number / Math.pow(options.binary ? 1024 : 1000, exponent)).toPrecision(3)); - const numberString = toLocaleString(number, options.locale); - - const unit = UNITS[exponent]; - - return prefix + numberString + ' ' + unit; -}; - - -/***/ }), - -/***/ 1127: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -"use strict"; - -const parseMilliseconds = __webpack_require__(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(' '); -}; - - -/***/ }), - -/***/ 399: -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { - -"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.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -const core = __importStar(__webpack_require__(2186)); -const ftp_deploy_1 = __webpack_require__(8347); -async function runDeployment() { - const args = { - server: core.getInput("server", { required: true }), - username: core.getInput("username", { required: true }), - password: core.getInput("password", { required: true }), - protocol: optionalProtocol("protocol", core.getInput("protocol")), - port: optionalInt("port", core.getInput("port")), - "local-dir": optionalString(core.getInput("local-dir")), - "server-dir": optionalString(core.getInput("server-dir")), - "state-name": optionalString(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")), - "security": optionalSecurity("security", core.getInput("security")) - }; - try { - await ftp_deploy_1.deploy(args); - } - catch (error) { - core.setFailed(error); - } -} -runDeployment(); -function optionalString(rawValue) { - if (rawValue.length === 0) { - return undefined; - } - return rawValue; -} -function optionalBoolean(argumentName, rawValue) { - if (rawValue.length === 0) { - 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, rawValue) { - if (rawValue.length === 0) { - 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, rawValue) { - if (rawValue.length === 0) { - 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 optionalSecurity(argumentName, rawValue) { - if (rawValue.length === 0) { - return undefined; - } - const cleanValue = rawValue.toLowerCase(); - if (cleanValue === "loose") { - return "loose"; - } - if (cleanValue === "strict") { - return "strict"; - } - core.setFailed(`${argumentName}: invalid parameter - you provided "${rawValue}". Try "loose" or "strict" instead.`); -} -function optionalInt(argumentName, rawValue) { - if (rawValue.length === 0) { - return undefined; - } - const valueAsNumber = parseFloat(rawValue); - 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, rawValue) { - if (rawValue.length === 0) { - return undefined; - } - // split value by space and comma - return rawValue.split(", "); -} - - -/***/ }), - -/***/ 6417: -/***/ ((module) => { - -"use strict"; -module.exports = require("crypto"); - -/***/ }), - -/***/ 5747: -/***/ ((module) => { - -"use strict"; -module.exports = require("fs"); - -/***/ }), - -/***/ 1631: -/***/ ((module) => { - -"use strict"; -module.exports = require("net"); - -/***/ }), - -/***/ 2087: -/***/ ((module) => { - -"use strict"; -module.exports = require("os"); - -/***/ }), - -/***/ 5622: -/***/ ((module) => { - -"use strict"; -module.exports = require("path"); - -/***/ }), - -/***/ 2413: -/***/ ((module) => { - -"use strict"; -module.exports = require("stream"); - -/***/ }), - -/***/ 4016: -/***/ ((module) => { - -"use strict"; -module.exports = require("tls"); - -/***/ }), - -/***/ 1669: -/***/ ((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/dist/main.js b/dist/main.js new file mode 100644 index 0000000..dcf35d6 --- /dev/null +++ b/dist/main.js @@ -0,0 +1,128 @@ +"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.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const core = __importStar(require("@actions/core")); +const ftp_deploy_1 = require("@samkirkland/ftp-deploy"); +async function runDeployment() { + const args = { + server: core.getInput("server", { required: true }), + username: core.getInput("username", { required: true }), + password: core.getInput("password", { required: true }), + port: optionalInt("port", core.getInput("port")), + protocol: optionalProtocol("protocol", core.getInput("protocol")), + "local-dir": optionalString(core.getInput("local-dir")), + "server-dir": optionalString(core.getInput("server-dir")), + "state-name": optionalString(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")), + "exclude": optionalStringArray("exclude", core.getInput("exclude")), + "log-level": optionalLogLevel("log-level", core.getInput("log-level")), + "security": optionalSecurity("security", core.getInput("security")) + }; + try { + await ftp_deploy_1.deploy(args); + } + catch (error) { + core.setFailed(error); + } +} +runDeployment(); +function optionalString(rawValue) { + if (rawValue.length === 0) { + return undefined; + } + return rawValue; +} +function optionalBoolean(argumentName, rawValue) { + if (rawValue.length === 0) { + 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, rawValue) { + if (rawValue.length === 0) { + 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, rawValue) { + if (rawValue.length === 0) { + return undefined; + } + const cleanValue = rawValue.toLowerCase(); + if (cleanValue === "minimal") { + return "minimal"; + } + if (cleanValue === "standard") { + return "standard"; + } + if (cleanValue === "verbose") { + return "verbose"; + } + core.setFailed(`${argumentName}: invalid parameter - you provided "${rawValue}". Try "minimal", "standard", or "verbose" instead.`); +} +function optionalSecurity(argumentName, rawValue) { + if (rawValue.length === 0) { + return undefined; + } + const cleanValue = rawValue.toLowerCase(); + if (cleanValue === "loose") { + return "loose"; + } + if (cleanValue === "strict") { + return "strict"; + } + core.setFailed(`${argumentName}: invalid parameter - you provided "${rawValue}". Try "loose" or "strict" instead.`); +} +function optionalInt(argumentName, rawValue) { + if (rawValue.length === 0) { + return undefined; + } + const valueAsNumber = parseFloat(rawValue); + 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, rawValue) { + if (rawValue.length === 0) { + return undefined; + } + // split value by space and comma + return rawValue.split(", "); +} diff --git a/package-lock.json b/package-lock.json index 9e28ece..1f50213 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { "name": "ftp-deploy-action", - "version": "1.0.0", + "version": "1.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { "@actions/core": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.5.tgz", - "integrity": "sha512-mwpoNjHSWWh0IiALdDEQi3tru124JKn0yVNziIBzTME8QRv7thwoghVuT1jBRjFvdtoHsqD58IRHy1nf86paRg==" + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.6.tgz", + "integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA==" }, "@babel/code-frame": { "version": "7.10.4", @@ -147,9 +147,9 @@ } }, "@samkirkland/ftp-deploy": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/@samkirkland/ftp-deploy/-/ftp-deploy-0.9.6.tgz", - "integrity": "sha512-6rUspSm4pSHUYRUwStvtAVIE7tkCvEs2bUcI9wtfN90COVdxUJ/hNwOF92jjhRqdp9287q+owVhbUpfJ29Twpg==", + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/@samkirkland/ftp-deploy/-/ftp-deploy-0.9.7.tgz", + "integrity": "sha512-4kdfy7tMCG4R+JGCK5zD3JwelL8au3Y1a3W5rBBgGRgPADBTCSS5hXDithKpKNO2ogMUBGxFfC9ebS/IkhPtqw==", "requires": { "basic-ftp": "^4.6.2", "lodash": "^4.17.20", @@ -240,12 +240,6 @@ "eslint-visitor-keys": "^2.0.0" } }, - "@vercel/ncc": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.24.0.tgz", - "integrity": "sha512-crqItMcIwCkvdXY/V3/TzrHJQx6nbIaRqE1cOopJhgGX6izvNov40SmD//nS5flfEvdK54YGjwVVq+zG6crjOg==", - "dev": true - }, "acorn": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", @@ -1384,9 +1378,9 @@ "integrity": "sha512-s1Iam6Gwz3JI5Hweaz4GoCD1WUNUIyzePFy5+Js2hjwGVt2Z79wNN+ZKOZ2vB6C+Xs6njyB84Z1IthQg8d9LxA==" }, "pretty-ms": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.0.tgz", - "integrity": "sha512-J3aPWiC5e9ZeZFuSeBraGxSkGMOvulSWsxDByOcbD1Pr75YL3LSNIKIb52WXbCLE1sS5s4inBBbryjF4Y05Ceg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", + "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", "requires": { "parse-ms": "^2.1.0" } diff --git a/package.json b/package.json index e70d4b0..a510176 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,12 @@ { "name": "ftp-deploy-action", - "version": "1.0.0", + "version": "1.0.1", "private": true, "description": "Automate deploying websites and more with this GitHub action", "main": "dist/main.js", "scripts": { - "build": "ncc build src/main.ts --no-cache", - "lint": "eslint src/**/*.ts", - "all": "npm run build && npm run lint" + "build": "tsc", + "lint": "eslint src/**/*.ts" }, "repository": { "type": "git", @@ -22,14 +21,13 @@ "author": "Sam Kirkland", "license": "MIT", "dependencies": { - "@actions/core": "^1.2.5", - "@samkirkland/ftp-deploy": "^0.9.6", + "@actions/core": "^1.2.6", + "@samkirkland/ftp-deploy": "^0.9.7", "ts-node-dev": "^1.0.0-pre.62" }, "devDependencies": { "@types/node": "^14.0.27", "@typescript-eslint/parser": "^4.0.1", - "@vercel/ncc": "^0.24.0", "eslint": "^7.8.1", "js-yaml": "^3.14.0", "typescript": "^4.0.2" diff --git a/src/main.ts b/src/main.ts index c0f5c05..f07fc6c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,14 +7,13 @@ async function runDeployment() { server: core.getInput("server", { required: true }), username: core.getInput("username", { required: true }), password: core.getInput("password", { required: true }), - protocol: optionalProtocol("protocol", core.getInput("protocol")), port: optionalInt("port", core.getInput("port")), + protocol: optionalProtocol("protocol", core.getInput("protocol")), "local-dir": optionalString(core.getInput("local-dir")), "server-dir": optionalString(core.getInput("server-dir")), "state-name": optionalString(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")), "security": optionalSecurity("security", core.getInput("security")) @@ -80,23 +79,23 @@ function optionalProtocol(argumentName: string, rawValue: string): "ftp" | "ftps core.setFailed(`${argumentName}: invalid parameter - you provided "${rawValue}". Try "ftp", "ftps", or "ftps-legacy" instead.`); } -function optionalLogLevel(argumentName: string, rawValue: string): "warn" | "info" | "debug" | undefined { +function optionalLogLevel(argumentName: string, rawValue: string): "minimal" | "standard" | "verbose" | undefined { if (rawValue.length === 0) { return undefined; } const cleanValue = rawValue.toLowerCase(); - if (cleanValue === "warn") { - return "warn"; + if (cleanValue === "minimal") { + return "minimal"; } - if (cleanValue === "info") { - return "info"; + if (cleanValue === "standard") { + return "standard"; } - if (cleanValue === "debug") { - return "debug"; + if (cleanValue === "verbose") { + return "verbose"; } - core.setFailed(`${argumentName}: invalid parameter - you provided "${rawValue}". Try "warn", "info", or "debug" instead.`); + core.setFailed(`${argumentName}: invalid parameter - you provided "${rawValue}". Try "minimal", "standard", or "verbose" instead.`); } function optionalSecurity(argumentName: string, rawValue: string): "loose" | "strict" | undefined { diff --git a/tsconfig.json b/tsconfig.json index 9941686..673198f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "target": "ES2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ + "target": "ES2019", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ - "outDir": "./lib", /* Redirect output structure to the directory. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ "strict": true, /* Enable all strict type-checking options. */ "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */