async downloadDir(srcDir, dstDir, options = { filter: null, useFastget: false }) {
    const getDownloadList = async (srcDir, filter) => {
      try {
        const listing = await this.list(srcDir);
        if (filter) {
          return listing.filter((item) =>
            filter(`${srcDir}/${item.name}`, item.type === 'd'),
          );
        }
        return listing;
      } catch (err) {
        throw err.custom ? err : this.fmtError(err.message, '_getDownloadList', err.code);
      }
    };

    const prepareDestination = (dst) => {
      try {
        const localCheck = haveLocalCreate(dst);
        if (!localCheck.status && localCheck.details === 'permission denied') {
          throw this.fmtError(
            `Bad path: ${dst}: ${localCheck.details}`,
            'prepareDestination',
            localCheck.code,
          );
        } else if (localCheck.status && !localCheck.type) {
          fs.mkdirSync(dst, { recursive: true });
        } else if (localCheck.status && localCheck.type !== 'd') {
          throw this.fmtError(
            `Bad path: ${dstDir}: not a directory`,
            '_prepareDestination',
            errorCode.badPath,
          );
        }
      } catch (err) {
        throw err.custom
          ? err
          : this.fmtError(err.message, '_prepareDestination', err.code);
      }
    };

    const downloadFiles = async (remotePath, localPath, fileList, useFastget) => {
      let listeners = addTempListeners(this, 'downloadFIles');

      try {
        const downloadList = [];
        for (const f of fileList) {
          const src = `${remotePath}/${f.name}`;
          const dst = join(localPath, f.name);
          downloadList.push([src, dst]);
        }
        const downloadGroups = partition(downloadList, this.promiseLimit);
        const func = useFastget ? this._fastGet.bind(this) : this.get.bind(this);
        const downloadResults = [];
        for (const group of downloadGroups) {
          const pList = [];
          for (const [src, dst] of group) {
            pList.push(func(src, dst, null, false));
            this.client.emit('download', { source: src, destination: dst });
          }
          const groupResults = await Promise.all(pList);
          for (const r of groupResults) {
            downloadResults.push(r);
          }
        }
        return downloadResults;
      } catch (e) {
        throw this.fmtError(
          `${e.message} ${srcDir} to ${dstDir}`,
          'downloadFiles',
          e.code,
        );
      } finally {
        removeTempListeners(this, listeners, 'downloadFiles');
      }
    };

    try {
      haveConnection(this, 'downloadDir');
      const downloadList = await getDownloadList(srcDir, options.filter);
      prepareDestination(dstDir);
      const fileDownloads = downloadList.filter((i) => i.type !== 'd');
      if (fileDownloads.length) {
        await downloadFiles(srcDir, dstDir, fileDownloads, options.useFastget);
      }
      const dirDownloads = downloadList.filter((i) => i.type === 'd');
      for (const d of dirDownloads) {
        const src = `${srcDir}/${d.name}`;
        const dst = join(dstDir, d.name);
        await this.downloadDir(src, dst, options);
      }
      return `${srcDir} downloaded to ${dstDir}`;
    } catch (err) {
      throw err.custom
        ? err
        : this.fmtError(`${err.message}: ${srcDir}`, 'downloadDir', err.code);
    }
  }
@async

Download the specified source directory to the specified destination
directory. All regular files and sub-directories are downloaded to the local
file system.
@param {String} srcDir - remote source directory
@param {String} dstDir - local destination directory
@param {Object} options - (Optional) Object with 2 supported properties,
'filter' and 'useFastget'. The filter property is a function of two
arguments. The first argument is the full path of the item to be downloaded
and the second argument is a boolean, which will be true if the target path
is for a directory. If the function returns true, the item will be
downloaded and excluded if teh function returns false.

@returns {Promise<Array>}