CoolFace
Apppublic

opusdev/vector-similarity-api

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes
index.js288 linesDownload Raw Back to readdirp
1'use strict';2 3const fs = require('fs');4const { Readable } = require('stream');5const sysPath = require('path');6const { promisify } = require('util');7const picomatch = require('picomatch');8 9const readdir = promisify(fs.readdir);10const stat = promisify(fs.stat);11const lstat = promisify(fs.lstat);12const realpath = promisify(fs.realpath);13 14/**15 * @typedef {Object} EntryInfo16 * @property {String} path17 * @property {String} fullPath18 * @property {fs.Stats=} stats19 * @property {fs.Dirent=} dirent20 * @property {String} basename21 */22 23const BANG = '!';24const RECURSIVE_ERROR_CODE = 'READDIRP_RECURSIVE_ERROR';25const NORMAL_FLOW_ERRORS = new Set(['ENOENT', 'EPERM', 'EACCES', 'ELOOP', RECURSIVE_ERROR_CODE]);26const FILE_TYPE = 'files';27const DIR_TYPE = 'directories';28const FILE_DIR_TYPE = 'files_directories';29const EVERYTHING_TYPE = 'all';30const ALL_TYPES = [FILE_TYPE, DIR_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE];31 32const isNormalFlowError = error => NORMAL_FLOW_ERRORS.has(error.code);33const [maj, min] = process.versions.node.split('.').slice(0, 2).map(n => Number.parseInt(n, 10));34const wantBigintFsStats = process.platform === 'win32' && (maj > 10 || (maj === 10 && min >= 5));35 36const normalizeFilter = filter => {37  if (filter === undefined) return;38  if (typeof filter === 'function') return filter;39 40  if (typeof filter === 'string') {41    const glob = picomatch(filter.trim());42    return entry => glob(entry.basename);43  }44 45  if (Array.isArray(filter)) {46    const positive = [];47    const negative = [];48    for (const item of filter) {49      const trimmed = item.trim();50      if (trimmed.charAt(0) === BANG) {51        negative.push(picomatch(trimmed.slice(1)));52      } else {53        positive.push(picomatch(trimmed));54      }55    }56 57    if (negative.length > 0) {58      if (positive.length > 0) {59        return entry =>60          positive.some(f => f(entry.basename)) && !negative.some(f => f(entry.basename));61      }62      return entry => !negative.some(f => f(entry.basename));63    }64    return entry => positive.some(f => f(entry.basename));65  }66};67 68class ReaddirpStream extends Readable {69  static get defaultOptions() {70    return {71      root: '.',72      /* eslint-disable no-unused-vars */73      fileFilter: (path) => true,74      directoryFilter: (path) => true,75      /* eslint-enable no-unused-vars */76      type: FILE_TYPE,77      lstat: false,78      depth: 2147483648,79      alwaysStat: false80    };81  }82 83  constructor(options = {}) {84    super({85      objectMode: true,86      autoDestroy: true,87      highWaterMark: options.highWaterMark || 409688    });89    const opts = { ...ReaddirpStream.defaultOptions, ...options };90    const { root, type } = opts;91 92    this._fileFilter = normalizeFilter(opts.fileFilter);93    this._directoryFilter = normalizeFilter(opts.directoryFilter);94 95    const statMethod = opts.lstat ? lstat : stat;96    // Use bigint stats if it's windows and stat() supports options (node 10+).97    if (wantBigintFsStats) {98      this._stat = path => statMethod(path, { bigint: true });99    } else {100      this._stat = statMethod;101    }102 103    this._maxDepth = opts.depth;104    this._wantsDir = [DIR_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE].includes(type);105    this._wantsFile = [FILE_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE].includes(type);106    this._wantsEverything = type === EVERYTHING_TYPE;107    this._root = sysPath.resolve(root);108    this._isDirent = ('Dirent' in fs) && !opts.alwaysStat;109    this._statsProp = this._isDirent ? 'dirent' : 'stats';110    this._rdOptions = { encoding: 'utf8', withFileTypes: this._isDirent };111 112    // Launch stream with one parent, the root dir.113    this.parents = [this._exploreDir(root, 1)];114    this.reading = false;115    this.parent = undefined;116  }117 118  async _read(batch) {119    if (this.reading) return;120    this.reading = true;121 122    try {123      while (!this.destroyed && batch > 0) {124        const { path, depth, files = [] } = this.parent || {};125 126        if (files.length > 0) {127          const slice = files.splice(0, batch).map(dirent => this._formatEntry(dirent, path));128          for (const entry of await Promise.all(slice)) {129            if (this.destroyed) return;130 131            const entryType = await this._getEntryType(entry);132            if (entryType === 'directory' && this._directoryFilter(entry)) {133              if (depth <= this._maxDepth) {134                this.parents.push(this._exploreDir(entry.fullPath, depth + 1));135              }136 137              if (this._wantsDir) {138                this.push(entry);139                batch--;140              }141            } else if ((entryType === 'file' || this._includeAsFile(entry)) && this._fileFilter(entry)) {142              if (this._wantsFile) {143                this.push(entry);144                batch--;145              }146            }147          }148        } else {149          const parent = this.parents.pop();150          if (!parent) {151            this.push(null);152            break;153          }154          this.parent = await parent;155          if (this.destroyed) return;156        }157      }158    } catch (error) {159      this.destroy(error);160    } finally {161      this.reading = false;162    }163  }164 165  async _exploreDir(path, depth) {166    let files;167    try {168      files = await readdir(path, this._rdOptions);169    } catch (error) {170      this._onError(error);171    }172    return { files, depth, path };173  }174 175  async _formatEntry(dirent, path) {176    let entry;177    try {178      const basename = this._isDirent ? dirent.name : dirent;179      const fullPath = sysPath.resolve(sysPath.join(path, basename));180      entry = { path: sysPath.relative(this._root, fullPath), fullPath, basename };181      entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);182    } catch (err) {183      this._onError(err);184    }185    return entry;186  }187 188  _onError(err) {189    if (isNormalFlowError(err) && !this.destroyed) {190      this.emit('warn', err);191    } else {192      this.destroy(err);193    }194  }195 196  async _getEntryType(entry) {197    // entry may be undefined, because a warning or an error were emitted198    // and the statsProp is undefined199    const stats = entry && entry[this._statsProp];200    if (!stats) {201      return;202    }203    if (stats.isFile()) {204      return 'file';205    }206    if (stats.isDirectory()) {207      return 'directory';208    }209    if (stats && stats.isSymbolicLink()) {210      const full = entry.fullPath;211      try {212        const entryRealPath = await realpath(full);213        const entryRealPathStats = await lstat(entryRealPath);214        if (entryRealPathStats.isFile()) {215          return 'file';216        }217        if (entryRealPathStats.isDirectory()) {218          const len = entryRealPath.length;219          if (full.startsWith(entryRealPath) && full.substr(len, 1) === sysPath.sep) {220            const recursiveError = new Error(221              `Circular symlink detected: "${full}" points to "${entryRealPath}"`222            );223            recursiveError.code = RECURSIVE_ERROR_CODE;224            return this._onError(recursiveError);225          }226          return 'directory';227        }228      } catch (error) {229        this._onError(error);230      }231    }232  }233 234  _includeAsFile(entry) {235    const stats = entry && entry[this._statsProp];236 237    return stats && this._wantsEverything && !stats.isDirectory();238  }239}240 241/**242 * @typedef {Object} ReaddirpArguments243 * @property {Function=} fileFilter244 * @property {Function=} directoryFilter245 * @property {String=} type246 * @property {Number=} depth247 * @property {String=} root248 * @property {Boolean=} lstat249 * @property {Boolean=} bigint250 */251 252/**253 * Main function which ends up calling readdirRec and reads all files and directories in given root recursively.254 * @param {String} root Root directory255 * @param {ReaddirpArguments=} options Options to specify root (start directory), filters and recursion depth256 */257const readdirp = (root, options = {}) => {258  let type = options.entryType || options.type;259  if (type === 'both') type = FILE_DIR_TYPE; // backwards-compatibility260  if (type) options.type = type;261  if (!root) {262    throw new Error('readdirp: root argument is required. Usage: readdirp(root, options)');263  } else if (typeof root !== 'string') {264    throw new TypeError('readdirp: root argument must be a string. Usage: readdirp(root, options)');265  } else if (type && !ALL_TYPES.includes(type)) {266    throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(', ')}`);267  }268 269  options.root = root;270  return new ReaddirpStream(options);271};272 273const readdirpPromise = (root, options = {}) => {274  return new Promise((resolve, reject) => {275    const files = [];276    readdirp(root, options)277      .on('data', entry => files.push(entry))278      .on('end', () => resolve(files))279      .on('error', error => reject(error));280  });281};282 283readdirp.promise = readdirpPromise;284readdirp.ReaddirpStream = ReaddirpStream;285readdirp.default = readdirp;286 287module.exports = readdirp;288