chaite
Version:
core for chatgpt-plugin and karin-plugin-chatgpt
1,561 lines (1,557 loc) • 45.1 kB
JavaScript
import { join, relative, resolve, sep } from "node:path";
import { lstat, open, readdir, realpath, stat } from "fs/promises";
import * as sysPath from "path";
import { EventEmitter } from "events";
import { stat as stat$1, unwatchFile, watch, watchFile } from "fs";
import { type } from "os";
import { lstat as lstat$1, readdir as readdir$1, realpath as realpath$1, stat as stat$2 } from "node:fs/promises";
import { Readable } from "node:stream";
//#region node_modules/.pnpm/readdirp@4.1.2/node_modules/readdirp/esm/index.js
const EntryTypes = {
FILE_TYPE: "files",
DIR_TYPE: "directories",
FILE_DIR_TYPE: "files_directories",
EVERYTHING_TYPE: "all"
};
const defaultOptions = {
root: ".",
fileFilter: (_entryInfo) => true,
directoryFilter: (_entryInfo) => true,
type: EntryTypes.FILE_TYPE,
lstat: false,
depth: 2147483648,
alwaysStat: false,
highWaterMark: 4096
};
Object.freeze(defaultOptions);
const RECURSIVE_ERROR_CODE = "READDIRP_RECURSIVE_ERROR";
const NORMAL_FLOW_ERRORS = new Set([
"ENOENT",
"EPERM",
"EACCES",
"ELOOP",
RECURSIVE_ERROR_CODE
]);
const ALL_TYPES = [
EntryTypes.DIR_TYPE,
EntryTypes.EVERYTHING_TYPE,
EntryTypes.FILE_DIR_TYPE,
EntryTypes.FILE_TYPE
];
const DIR_TYPES = new Set([
EntryTypes.DIR_TYPE,
EntryTypes.EVERYTHING_TYPE,
EntryTypes.FILE_DIR_TYPE
]);
const FILE_TYPES = new Set([
EntryTypes.EVERYTHING_TYPE,
EntryTypes.FILE_DIR_TYPE,
EntryTypes.FILE_TYPE
]);
const isNormalFlowError = (error) => NORMAL_FLOW_ERRORS.has(error.code);
const wantBigintFsStats = process.platform === "win32";
const emptyFn = (_entryInfo) => true;
const normalizeFilter = (filter) => {
if (filter === void 0) return emptyFn;
if (typeof filter === "function") return filter;
if (typeof filter === "string") {
const fl = filter.trim();
return (entry) => entry.basename === fl;
}
if (Array.isArray(filter)) {
const trItems = filter.map((item) => item.trim());
return (entry) => trItems.some((f) => entry.basename === f);
}
return emptyFn;
};
/** Readable readdir stream, emitting new files as they're being listed. */
var ReaddirpStream = class extends Readable {
constructor(options = {}) {
super({
objectMode: true,
autoDestroy: true,
highWaterMark: options.highWaterMark
});
const opts = {
...defaultOptions,
...options
};
const { root, type: type$1 } = opts;
this._fileFilter = normalizeFilter(opts.fileFilter);
this._directoryFilter = normalizeFilter(opts.directoryFilter);
const statMethod = opts.lstat ? lstat$1 : stat$2;
if (wantBigintFsStats) this._stat = (path$2) => statMethod(path$2, { bigint: true });
else this._stat = statMethod;
this._maxDepth = opts.depth ?? defaultOptions.depth;
this._wantsDir = type$1 ? DIR_TYPES.has(type$1) : false;
this._wantsFile = type$1 ? FILE_TYPES.has(type$1) : false;
this._wantsEverything = type$1 === EntryTypes.EVERYTHING_TYPE;
this._root = resolve(root);
this._isDirent = !opts.alwaysStat;
this._statsProp = this._isDirent ? "dirent" : "stats";
this._rdOptions = {
encoding: "utf8",
withFileTypes: this._isDirent
};
this.parents = [this._exploreDir(root, 1)];
this.reading = false;
this.parent = void 0;
}
async _read(batch) {
if (this.reading) return;
this.reading = true;
try {
while (!this.destroyed && batch > 0) {
const par = this.parent;
const fil = par && par.files;
if (fil && fil.length > 0) {
const { path: path$2, depth } = par;
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path$2));
const awaited = await Promise.all(slice);
for (const entry of awaited) {
if (!entry) continue;
if (this.destroyed) return;
const entryType = await this._getEntryType(entry);
if (entryType === "directory" && this._directoryFilter(entry)) {
if (depth <= this._maxDepth) this.parents.push(this._exploreDir(entry.fullPath, depth + 1));
if (this._wantsDir) {
this.push(entry);
batch--;
}
} else if ((entryType === "file" || this._includeAsFile(entry)) && this._fileFilter(entry)) {
if (this._wantsFile) {
this.push(entry);
batch--;
}
}
}
} else {
const parent = this.parents.pop();
if (!parent) {
this.push(null);
break;
}
this.parent = await parent;
if (this.destroyed) return;
}
}
} catch (error) {
this.destroy(error);
} finally {
this.reading = false;
}
}
async _exploreDir(path$2, depth) {
let files;
try {
files = await readdir$1(path$2, this._rdOptions);
} catch (error) {
this._onError(error);
}
return {
files,
depth,
path: path$2
};
}
async _formatEntry(dirent, path$2) {
let entry;
const basename$1 = this._isDirent ? dirent.name : dirent;
try {
const fullPath = resolve(join(path$2, basename$1));
entry = {
path: relative(this._root, fullPath),
fullPath,
basename: basename$1
};
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
} catch (err) {
this._onError(err);
return;
}
return entry;
}
_onError(err) {
if (isNormalFlowError(err) && !this.destroyed) this.emit("warn", err);
else this.destroy(err);
}
async _getEntryType(entry) {
if (!entry && this._statsProp in entry) return "";
const stats = entry[this._statsProp];
if (stats.isFile()) return "file";
if (stats.isDirectory()) return "directory";
if (stats && stats.isSymbolicLink()) {
const full = entry.fullPath;
try {
const entryRealPath = await realpath$1(full);
const entryRealPathStats = await lstat$1(entryRealPath);
if (entryRealPathStats.isFile()) return "file";
if (entryRealPathStats.isDirectory()) {
const len = entryRealPath.length;
if (full.startsWith(entryRealPath) && full.substr(len, 1) === sep) {
const recursiveError = /* @__PURE__ */ new Error(`Circular symlink detected: "${full}" points to "${entryRealPath}"`);
recursiveError.code = RECURSIVE_ERROR_CODE;
return this._onError(recursiveError);
}
return "directory";
}
} catch (error) {
this._onError(error);
return "";
}
}
}
_includeAsFile(entry) {
const stats = entry && entry[this._statsProp];
return stats && this._wantsEverything && !stats.isDirectory();
}
};
/**
* Streaming version: Reads all files and directories in given root recursively.
* Consumes ~constant small amount of RAM.
* @param root Root directory
* @param options Options to specify root (start directory), filters and recursion depth
*/
function readdirp(root, options = {}) {
let type$1 = options.entryType || options.type;
if (type$1 === "both") type$1 = EntryTypes.FILE_DIR_TYPE;
if (type$1) options.type = type$1;
if (!root) throw new Error("readdirp: root argument is required. Usage: readdirp(root, options)");
else if (typeof root !== "string") throw new TypeError("readdirp: root argument must be a string. Usage: readdirp(root, options)");
else if (type$1 && !ALL_TYPES.includes(type$1)) throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(", ")}`);
options.root = root;
return new ReaddirpStream(options);
}
//#endregion
//#region node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/handler.js
const STR_DATA = "data";
const STR_END = "end";
const STR_CLOSE = "close";
const EMPTY_FN = () => {};
const pl = process.platform;
const isWindows = pl === "win32";
const isMacos = pl === "darwin";
const isLinux = pl === "linux";
const isFreeBSD = pl === "freebsd";
const isIBMi = type() === "OS400";
const EVENTS = {
ALL: "all",
READY: "ready",
ADD: "add",
CHANGE: "change",
ADD_DIR: "addDir",
UNLINK: "unlink",
UNLINK_DIR: "unlinkDir",
RAW: "raw",
ERROR: "error"
};
const EV = EVENTS;
const THROTTLE_MODE_WATCH = "watch";
const statMethods = {
lstat,
stat
};
const KEY_LISTENERS = "listeners";
const KEY_ERR = "errHandlers";
const KEY_RAW = "rawEmitters";
const HANDLER_KEYS = [
KEY_LISTENERS,
KEY_ERR,
KEY_RAW
];
const binaryExtensions = new Set([
"3dm",
"3ds",
"3g2",
"3gp",
"7z",
"a",
"aac",
"adp",
"afdesign",
"afphoto",
"afpub",
"ai",
"aif",
"aiff",
"alz",
"ape",
"apk",
"appimage",
"ar",
"arj",
"asf",
"au",
"avi",
"bak",
"baml",
"bh",
"bin",
"bk",
"bmp",
"btif",
"bz2",
"bzip2",
"cab",
"caf",
"cgm",
"class",
"cmx",
"cpio",
"cr2",
"cur",
"dat",
"dcm",
"deb",
"dex",
"djvu",
"dll",
"dmg",
"dng",
"doc",
"docm",
"docx",
"dot",
"dotm",
"dra",
"DS_Store",
"dsk",
"dts",
"dtshd",
"dvb",
"dwg",
"dxf",
"ecelp4800",
"ecelp7470",
"ecelp9600",
"egg",
"eol",
"eot",
"epub",
"exe",
"f4v",
"fbs",
"fh",
"fla",
"flac",
"flatpak",
"fli",
"flv",
"fpx",
"fst",
"fvt",
"g3",
"gh",
"gif",
"graffle",
"gz",
"gzip",
"h261",
"h263",
"h264",
"icns",
"ico",
"ief",
"img",
"ipa",
"iso",
"jar",
"jpeg",
"jpg",
"jpgv",
"jpm",
"jxr",
"key",
"ktx",
"lha",
"lib",
"lvp",
"lz",
"lzh",
"lzma",
"lzo",
"m3u",
"m4a",
"m4v",
"mar",
"mdi",
"mht",
"mid",
"midi",
"mj2",
"mka",
"mkv",
"mmr",
"mng",
"mobi",
"mov",
"movie",
"mp3",
"mp4",
"mp4a",
"mpeg",
"mpg",
"mpga",
"mxu",
"nef",
"npx",
"numbers",
"nupkg",
"o",
"odp",
"ods",
"odt",
"oga",
"ogg",
"ogv",
"otf",
"ott",
"pages",
"pbm",
"pcx",
"pdb",
"pdf",
"pea",
"pgm",
"pic",
"png",
"pnm",
"pot",
"potm",
"potx",
"ppa",
"ppam",
"ppm",
"pps",
"ppsm",
"ppsx",
"ppt",
"pptm",
"pptx",
"psd",
"pya",
"pyc",
"pyo",
"pyv",
"qt",
"rar",
"ras",
"raw",
"resources",
"rgb",
"rip",
"rlc",
"rmf",
"rmvb",
"rpm",
"rtf",
"rz",
"s3m",
"s7z",
"scpt",
"sgi",
"shar",
"snap",
"sil",
"sketch",
"slk",
"smv",
"snk",
"so",
"stl",
"suo",
"sub",
"swf",
"tar",
"tbz",
"tbz2",
"tga",
"tgz",
"thmx",
"tif",
"tiff",
"tlz",
"ttc",
"ttf",
"txz",
"udf",
"uvh",
"uvi",
"uvm",
"uvp",
"uvs",
"uvu",
"viv",
"vob",
"war",
"wav",
"wax",
"wbmp",
"wdp",
"weba",
"webm",
"webp",
"whl",
"wim",
"wm",
"wma",
"wmv",
"wmx",
"woff",
"woff2",
"wrm",
"wvx",
"xbm",
"xif",
"xla",
"xlam",
"xls",
"xlsb",
"xlsm",
"xlsx",
"xlt",
"xltm",
"xltx",
"xm",
"xmind",
"xpi",
"xpm",
"xwd",
"xz",
"z",
"zip",
"zipx"
]);
const isBinaryPath = (filePath) => binaryExtensions.has(sysPath.extname(filePath).slice(1).toLowerCase());
const foreach = (val, fn) => {
if (val instanceof Set) val.forEach(fn);
else fn(val);
};
const addAndConvert = (main, prop, item) => {
let container = main[prop];
if (!(container instanceof Set)) main[prop] = container = new Set([container]);
container.add(item);
};
const clearItem = (cont) => (key) => {
const set = cont[key];
if (set instanceof Set) set.clear();
else delete cont[key];
};
const delFromSet = (main, prop, item) => {
const container = main[prop];
if (container instanceof Set) container.delete(item);
else if (container === item) delete main[prop];
};
const isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
const FsWatchInstances = /* @__PURE__ */ new Map();
/**
* Instantiates the fs_watch interface
* @param path to be watched
* @param options to be passed to fs_watch
* @param listener main event handler
* @param errHandler emits info about errors
* @param emitRaw emits raw event data
* @returns {NativeFsWatcher}
*/
function createFsWatchInstance(path$2, options, listener, errHandler, emitRaw) {
const handleEvent = (rawEvent, evPath) => {
listener(path$2);
emitRaw(rawEvent, evPath, { watchedPath: path$2 });
if (evPath && path$2 !== evPath) fsWatchBroadcast(sysPath.resolve(path$2, evPath), KEY_LISTENERS, sysPath.join(path$2, evPath));
};
try {
return watch(path$2, { persistent: options.persistent }, handleEvent);
} catch (error) {
errHandler(error);
return;
}
}
/**
* Helper for passing fs_watch event data to a collection of listeners
* @param fullPath absolute path bound to fs_watch instance
*/
const fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
const cont = FsWatchInstances.get(fullPath);
if (!cont) return;
foreach(cont[listenerType], (listener) => {
listener(val1, val2, val3);
});
};
/**
* Instantiates the fs_watch interface or binds listeners
* to an existing one covering the same file system entry
* @param path
* @param fullPath absolute path
* @param options to be passed to fs_watch
* @param handlers container for event listener functions
*/
const setFsWatchListener = (path$2, fullPath, options, handlers) => {
const { listener, errHandler, rawEmitter } = handlers;
let cont = FsWatchInstances.get(fullPath);
let watcher;
if (!options.persistent) {
watcher = createFsWatchInstance(path$2, options, listener, errHandler, rawEmitter);
if (!watcher) return;
return watcher.close.bind(watcher);
}
if (cont) {
addAndConvert(cont, KEY_LISTENERS, listener);
addAndConvert(cont, KEY_ERR, errHandler);
addAndConvert(cont, KEY_RAW, rawEmitter);
} else {
watcher = createFsWatchInstance(path$2, options, fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS), errHandler, fsWatchBroadcast.bind(null, fullPath, KEY_RAW));
if (!watcher) return;
watcher.on(EV.ERROR, async (error) => {
const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR);
if (cont) cont.watcherUnusable = true;
if (isWindows && error.code === "EPERM") try {
await (await open(path$2, "r")).close();
broadcastErr(error);
} catch (err) {}
else broadcastErr(error);
});
cont = {
listeners: listener,
errHandlers: errHandler,
rawEmitters: rawEmitter,
watcher
};
FsWatchInstances.set(fullPath, cont);
}
return () => {
delFromSet(cont, KEY_LISTENERS, listener);
delFromSet(cont, KEY_ERR, errHandler);
delFromSet(cont, KEY_RAW, rawEmitter);
if (isEmptySet(cont.listeners)) {
cont.watcher.close();
FsWatchInstances.delete(fullPath);
HANDLER_KEYS.forEach(clearItem(cont));
cont.watcher = void 0;
Object.freeze(cont);
}
};
};
const FsWatchFileInstances = /* @__PURE__ */ new Map();
/**
* Instantiates the fs_watchFile interface or binds listeners
* to an existing one covering the same file system entry
* @param path to be watched
* @param fullPath absolute path
* @param options options to be passed to fs_watchFile
* @param handlers container for event listener functions
* @returns closer
*/
const setFsWatchFileListener = (path$2, fullPath, options, handlers) => {
const { listener, rawEmitter } = handlers;
let cont = FsWatchFileInstances.get(fullPath);
const copts = cont && cont.options;
if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) {
unwatchFile(fullPath);
cont = void 0;
}
if (cont) {
addAndConvert(cont, KEY_LISTENERS, listener);
addAndConvert(cont, KEY_RAW, rawEmitter);
} else {
cont = {
listeners: listener,
rawEmitters: rawEmitter,
options,
watcher: watchFile(fullPath, options, (curr, prev) => {
foreach(cont.rawEmitters, (rawEmitter$1) => {
rawEmitter$1(EV.CHANGE, fullPath, {
curr,
prev
});
});
const currmtime = curr.mtimeMs;
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) foreach(cont.listeners, (listener$1) => listener$1(path$2, curr));
})
};
FsWatchFileInstances.set(fullPath, cont);
}
return () => {
delFromSet(cont, KEY_LISTENERS, listener);
delFromSet(cont, KEY_RAW, rawEmitter);
if (isEmptySet(cont.listeners)) {
FsWatchFileInstances.delete(fullPath);
unwatchFile(fullPath);
cont.options = cont.watcher = void 0;
Object.freeze(cont);
}
};
};
/**
* @mixin
*/
var NodeFsHandler = class {
constructor(fsW) {
this.fsw = fsW;
this._boundHandleError = (error) => fsW._handleError(error);
}
/**
* Watch file for changes with fs_watchFile or fs_watch.
* @param path to file or dir
* @param listener on fs change
* @returns closer for the watcher instance
*/
_watchWithNodeFs(path$2, listener) {
const opts = this.fsw.options;
const directory = sysPath.dirname(path$2);
const basename$1 = sysPath.basename(path$2);
this.fsw._getWatchedDir(directory).add(basename$1);
const absolutePath = sysPath.resolve(path$2);
const options = { persistent: opts.persistent };
if (!listener) listener = EMPTY_FN;
let closer;
if (opts.usePolling) {
options.interval = opts.interval !== opts.binaryInterval && isBinaryPath(basename$1) ? opts.binaryInterval : opts.interval;
closer = setFsWatchFileListener(path$2, absolutePath, options, {
listener,
rawEmitter: this.fsw._emitRaw
});
} else closer = setFsWatchListener(path$2, absolutePath, options, {
listener,
errHandler: this._boundHandleError,
rawEmitter: this.fsw._emitRaw
});
return closer;
}
/**
* Watch a file and emit add event if warranted.
* @returns closer for the watcher instance
*/
_handleFile(file, stats, initialAdd) {
if (this.fsw.closed) return;
const dirname = sysPath.dirname(file);
const basename$1 = sysPath.basename(file);
const parent = this.fsw._getWatchedDir(dirname);
let prevStats = stats;
if (parent.has(basename$1)) return;
const listener = async (path$2, newStats) => {
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5)) return;
if (!newStats || newStats.mtimeMs === 0) try {
const newStats$1 = await stat(file);
if (this.fsw.closed) return;
const at = newStats$1.atimeMs;
const mt = newStats$1.mtimeMs;
if (!at || at <= mt || mt !== prevStats.mtimeMs) this.fsw._emit(EV.CHANGE, file, newStats$1);
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats$1.ino) {
this.fsw._closeFile(path$2);
prevStats = newStats$1;
const closer$1 = this._watchWithNodeFs(file, listener);
if (closer$1) this.fsw._addPathCloser(path$2, closer$1);
} else prevStats = newStats$1;
} catch (error) {
this.fsw._remove(dirname, basename$1);
}
else if (parent.has(basename$1)) {
const at = newStats.atimeMs;
const mt = newStats.mtimeMs;
if (!at || at <= mt || mt !== prevStats.mtimeMs) this.fsw._emit(EV.CHANGE, file, newStats);
prevStats = newStats;
}
};
const closer = this._watchWithNodeFs(file, listener);
if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) {
if (!this.fsw._throttle(EV.ADD, file, 0)) return;
this.fsw._emit(EV.ADD, file, stats);
}
return closer;
}
/**
* Handle symlinks encountered while reading a dir.
* @param entry returned by readdirp
* @param directory path of dir being read
* @param path of this item
* @param item basename of this item
* @returns true if no more processing is needed for this entry.
*/
async _handleSymlink(entry, directory, path$2, item) {
if (this.fsw.closed) return;
const full = entry.fullPath;
const dir = this.fsw._getWatchedDir(directory);
if (!this.fsw.options.followSymlinks) {
this.fsw._incrReadyCount();
let linkPath;
try {
linkPath = await realpath(path$2);
} catch (e) {
this.fsw._emitReady();
return true;
}
if (this.fsw.closed) return;
if (dir.has(item)) {
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
this.fsw._symlinkPaths.set(full, linkPath);
this.fsw._emit(EV.CHANGE, path$2, entry.stats);
}
} else {
dir.add(item);
this.fsw._symlinkPaths.set(full, linkPath);
this.fsw._emit(EV.ADD, path$2, entry.stats);
}
this.fsw._emitReady();
return true;
}
if (this.fsw._symlinkPaths.has(full)) return true;
this.fsw._symlinkPaths.set(full, true);
}
_handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {
directory = sysPath.join(directory, "");
throttler = this.fsw._throttle("readdir", directory, 1e3);
if (!throttler) return;
const previous = this.fsw._getWatchedDir(wh.path);
const current = /* @__PURE__ */ new Set();
let stream = this.fsw._readdirp(directory, {
fileFilter: (entry) => wh.filterPath(entry),
directoryFilter: (entry) => wh.filterDir(entry)
});
if (!stream) return;
stream.on(STR_DATA, async (entry) => {
if (this.fsw.closed) {
stream = void 0;
return;
}
const item = entry.path;
let path$2 = sysPath.join(directory, item);
current.add(item);
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path$2, item)) return;
if (this.fsw.closed) {
stream = void 0;
return;
}
if (item === target || !target && !previous.has(item)) {
this.fsw._incrReadyCount();
path$2 = sysPath.join(dir, sysPath.relative(dir, path$2));
this._addToNodeFs(path$2, initialAdd, wh, depth + 1);
}
}).on(EV.ERROR, this._boundHandleError);
return new Promise((resolve$1, reject) => {
if (!stream) return reject();
stream.once(STR_END, () => {
if (this.fsw.closed) {
stream = void 0;
return;
}
const wasThrottled = throttler ? throttler.clear() : false;
resolve$1(void 0);
previous.getChildren().filter((item) => {
return item !== directory && !current.has(item);
}).forEach((item) => {
this.fsw._remove(directory, item);
});
stream = void 0;
if (wasThrottled) this._handleRead(directory, false, wh, target, dir, depth, throttler);
});
});
}
/**
* Read directory to add / remove files from `@watched` list and re-read it on change.
* @param dir fs path
* @param stats
* @param initialAdd
* @param depth relative to user-supplied path
* @param target child path targeted for watch
* @param wh Common watch helpers for this path
* @param realpath
* @returns closer for the watcher instance.
*/
async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath$2) {
const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir));
const tracked = parentDir.has(sysPath.basename(dir));
if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) this.fsw._emit(EV.ADD_DIR, dir, stats);
parentDir.add(sysPath.basename(dir));
this.fsw._getWatchedDir(dir);
let throttler;
let closer;
const oDepth = this.fsw.options.depth;
if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath$2)) {
if (!target) {
await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler);
if (this.fsw.closed) return;
}
closer = this._watchWithNodeFs(dir, (dirPath, stats$1) => {
if (stats$1 && stats$1.mtimeMs === 0) return;
this._handleRead(dirPath, false, wh, target, dir, depth, throttler);
});
}
return closer;
}
/**
* Handle added file, directory, or glob pattern.
* Delegates call to _handleFile / _handleDir after checks.
* @param path to file or ir
* @param initialAdd was the file added at watch instantiation?
* @param priorWh depth relative to user-supplied path
* @param depth Child path actually targeted for watch
* @param target Child path actually targeted for watch
*/
async _addToNodeFs(path$2, initialAdd, priorWh, depth, target) {
const ready = this.fsw._emitReady;
if (this.fsw._isIgnored(path$2) || this.fsw.closed) {
ready();
return false;
}
const wh = this.fsw._getWatchHelpers(path$2);
if (priorWh) {
wh.filterPath = (entry) => priorWh.filterPath(entry);
wh.filterDir = (entry) => priorWh.filterDir(entry);
}
try {
const stats = await statMethods[wh.statMethod](wh.watchPath);
if (this.fsw.closed) return;
if (this.fsw._isIgnored(wh.watchPath, stats)) {
ready();
return false;
}
const follow = this.fsw.options.followSymlinks;
let closer;
if (stats.isDirectory()) {
const absPath = sysPath.resolve(path$2);
const targetPath = follow ? await realpath(path$2) : path$2;
if (this.fsw.closed) return;
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
if (this.fsw.closed) return;
if (absPath !== targetPath && targetPath !== void 0) this.fsw._symlinkPaths.set(absPath, targetPath);
} else if (stats.isSymbolicLink()) {
const targetPath = follow ? await realpath(path$2) : path$2;
if (this.fsw.closed) return;
const parent = sysPath.dirname(wh.watchPath);
this.fsw._getWatchedDir(parent).add(wh.watchPath);
this.fsw._emit(EV.ADD, wh.watchPath, stats);
closer = await this._handleDir(parent, stats, initialAdd, depth, path$2, wh, targetPath);
if (this.fsw.closed) return;
if (targetPath !== void 0) this.fsw._symlinkPaths.set(sysPath.resolve(path$2), targetPath);
} else closer = this._handleFile(wh.watchPath, stats, initialAdd);
ready();
if (closer) this.fsw._addPathCloser(path$2, closer);
return false;
} catch (error) {
if (this.fsw._handleError(error)) {
ready();
return path$2;
}
}
}
};
//#endregion
//#region node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/index.js
/*! chokidar - MIT License (c) 2012 Paul Miller (paulmillr.com) */
const SLASH = "/";
const SLASH_SLASH = "//";
const ONE_DOT = ".";
const TWO_DOTS = "..";
const STRING_TYPE = "string";
const BACK_SLASH_RE = /\\/g;
const DOUBLE_SLASH_RE = /\/\//;
const DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/;
const REPLACER_RE = /^\.[/\\]/;
function arrify(item) {
return Array.isArray(item) ? item : [item];
}
const isMatcherObject = (matcher) => typeof matcher === "object" && matcher !== null && !(matcher instanceof RegExp);
function createPattern(matcher) {
if (typeof matcher === "function") return matcher;
if (typeof matcher === "string") return (string) => matcher === string;
if (matcher instanceof RegExp) return (string) => matcher.test(string);
if (typeof matcher === "object" && matcher !== null) return (string) => {
if (matcher.path === string) return true;
if (matcher.recursive) {
const relative$1 = sysPath.relative(matcher.path, string);
if (!relative$1) return false;
return !relative$1.startsWith("..") && !sysPath.isAbsolute(relative$1);
}
return false;
};
return () => false;
}
function normalizePath(path$2) {
if (typeof path$2 !== "string") throw new Error("string expected");
path$2 = sysPath.normalize(path$2);
path$2 = path$2.replace(/\\/g, "/");
let prepend = false;
if (path$2.startsWith("//")) prepend = true;
const DOUBLE_SLASH_RE$1 = /\/\//;
while (path$2.match(DOUBLE_SLASH_RE$1)) path$2 = path$2.replace(DOUBLE_SLASH_RE$1, "/");
if (prepend) path$2 = "/" + path$2;
return path$2;
}
function matchPatterns(patterns, testString, stats) {
const path$2 = normalizePath(testString);
for (let index = 0; index < patterns.length; index++) {
const pattern = patterns[index];
if (pattern(path$2, stats)) return true;
}
return false;
}
function anymatch(matchers, testString) {
if (matchers == null) throw new TypeError("anymatch: specify first argument");
const patterns = arrify(matchers).map((matcher) => createPattern(matcher));
if (testString == null) return (testString$1, stats) => {
return matchPatterns(patterns, testString$1, stats);
};
return matchPatterns(patterns, testString);
}
const unifyPaths = (paths_) => {
const paths = arrify(paths_).flat();
if (!paths.every((p) => typeof p === STRING_TYPE)) throw new TypeError(`Non-string provided as watch path: ${paths}`);
return paths.map(normalizePathToUnix);
};
const toUnix = (string) => {
let str = string.replace(BACK_SLASH_RE, SLASH);
let prepend = false;
if (str.startsWith(SLASH_SLASH)) prepend = true;
while (str.match(DOUBLE_SLASH_RE)) str = str.replace(DOUBLE_SLASH_RE, SLASH);
if (prepend) str = SLASH + str;
return str;
};
const normalizePathToUnix = (path$2) => toUnix(sysPath.normalize(toUnix(path$2)));
const normalizeIgnored = (cwd = "") => (path$2) => {
if (typeof path$2 === "string") return normalizePathToUnix(sysPath.isAbsolute(path$2) ? path$2 : sysPath.join(cwd, path$2));
else return path$2;
};
const getAbsolutePath = (path$2, cwd) => {
if (sysPath.isAbsolute(path$2)) return path$2;
return sysPath.join(cwd, path$2);
};
const EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
/**
* Directory entry.
*/
var DirEntry = class {
constructor(dir, removeWatcher) {
this.path = dir;
this._removeWatcher = removeWatcher;
this.items = /* @__PURE__ */ new Set();
}
add(item) {
const { items } = this;
if (!items) return;
if (item !== ONE_DOT && item !== TWO_DOTS) items.add(item);
}
async remove(item) {
const { items } = this;
if (!items) return;
items.delete(item);
if (items.size > 0) return;
const dir = this.path;
try {
await readdir(dir);
} catch (err) {
if (this._removeWatcher) this._removeWatcher(sysPath.dirname(dir), sysPath.basename(dir));
}
}
has(item) {
const { items } = this;
if (!items) return;
return items.has(item);
}
getChildren() {
const { items } = this;
if (!items) return [];
return [...items.values()];
}
dispose() {
this.items.clear();
this.path = "";
this._removeWatcher = EMPTY_FN;
this.items = EMPTY_SET;
Object.freeze(this);
}
};
const STAT_METHOD_F = "stat";
const STAT_METHOD_L = "lstat";
var WatchHelper = class {
constructor(path$2, follow, fsw) {
this.fsw = fsw;
const watchPath = path$2;
this.path = path$2 = path$2.replace(REPLACER_RE, "");
this.watchPath = watchPath;
this.fullWatchPath = sysPath.resolve(watchPath);
this.dirParts = [];
this.dirParts.forEach((parts) => {
if (parts.length > 1) parts.pop();
});
this.followSymlinks = follow;
this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;
}
entryPath(entry) {
return sysPath.join(this.watchPath, sysPath.relative(this.watchPath, entry.fullPath));
}
filterPath(entry) {
const { stats } = entry;
if (stats && stats.isSymbolicLink()) return this.filterDir(entry);
const resolvedPath = this.entryPath(entry);
return this.fsw._isntIgnored(resolvedPath, stats) && this.fsw._hasReadPermissions(stats);
}
filterDir(entry) {
return this.fsw._isntIgnored(this.entryPath(entry), entry.stats);
}
};
/**
* Watches files & directories for changes. Emitted events:
* `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `all`, `error`
*
* new FSWatcher()
* .add(directories)
* .on('add', path => log('File', path, 'was added'))
*/
var FSWatcher = class extends EventEmitter {
constructor(_opts = {}) {
super();
this.closed = false;
this._closers = /* @__PURE__ */ new Map();
this._ignoredPaths = /* @__PURE__ */ new Set();
this._throttled = /* @__PURE__ */ new Map();
this._streams = /* @__PURE__ */ new Set();
this._symlinkPaths = /* @__PURE__ */ new Map();
this._watched = /* @__PURE__ */ new Map();
this._pendingWrites = /* @__PURE__ */ new Map();
this._pendingUnlinks = /* @__PURE__ */ new Map();
this._readyCount = 0;
this._readyEmitted = false;
const awf = _opts.awaitWriteFinish;
const DEF_AWF = {
stabilityThreshold: 2e3,
pollInterval: 100
};
const opts = {
persistent: true,
ignoreInitial: false,
ignorePermissionErrors: false,
interval: 100,
binaryInterval: 300,
followSymlinks: true,
usePolling: false,
atomic: true,
..._opts,
ignored: _opts.ignored ? arrify(_opts.ignored) : arrify([]),
awaitWriteFinish: awf === true ? DEF_AWF : typeof awf === "object" ? {
...DEF_AWF,
...awf
} : false
};
if (isIBMi) opts.usePolling = true;
if (opts.atomic === void 0) opts.atomic = !opts.usePolling;
const envPoll = process.env.CHOKIDAR_USEPOLLING;
if (envPoll !== void 0) {
const envLower = envPoll.toLowerCase();
if (envLower === "false" || envLower === "0") opts.usePolling = false;
else if (envLower === "true" || envLower === "1") opts.usePolling = true;
else opts.usePolling = !!envLower;
}
const envInterval = process.env.CHOKIDAR_INTERVAL;
if (envInterval) opts.interval = Number.parseInt(envInterval, 10);
let readyCalls = 0;
this._emitReady = () => {
readyCalls++;
if (readyCalls >= this._readyCount) {
this._emitReady = EMPTY_FN;
this._readyEmitted = true;
process.nextTick(() => this.emit(EVENTS.READY));
}
};
this._emitRaw = (...args) => this.emit(EVENTS.RAW, ...args);
this._boundRemove = this._remove.bind(this);
this.options = opts;
this._nodeFsHandler = new NodeFsHandler(this);
Object.freeze(opts);
}
_addIgnoredPath(matcher) {
if (isMatcherObject(matcher)) {
for (const ignored of this._ignoredPaths) if (isMatcherObject(ignored) && ignored.path === matcher.path && ignored.recursive === matcher.recursive) return;
}
this._ignoredPaths.add(matcher);
}
_removeIgnoredPath(matcher) {
this._ignoredPaths.delete(matcher);
if (typeof matcher === "string") {
for (const ignored of this._ignoredPaths) if (isMatcherObject(ignored) && ignored.path === matcher) this._ignoredPaths.delete(ignored);
}
}
/**
* Adds paths to be watched on an existing FSWatcher instance.
* @param paths_ file or file list. Other arguments are unused
*/
add(paths_, _origAdd, _internal) {
const { cwd } = this.options;
this.closed = false;
this._closePromise = void 0;
let paths = unifyPaths(paths_);
if (cwd) paths = paths.map((path$2) => {
return getAbsolutePath(path$2, cwd);
});
paths.forEach((path$2) => {
this._removeIgnoredPath(path$2);
});
this._userIgnored = void 0;
if (!this._readyCount) this._readyCount = 0;
this._readyCount += paths.length;
Promise.all(paths.map(async (path$2) => {
const res = await this._nodeFsHandler._addToNodeFs(path$2, !_internal, void 0, 0, _origAdd);
if (res) this._emitReady();
return res;
})).then((results) => {
if (this.closed) return;
results.forEach((item) => {
if (item) this.add(sysPath.dirname(item), sysPath.basename(_origAdd || item));
});
});
return this;
}
/**
* Close watchers or start ignoring events from specified paths.
*/
unwatch(paths_) {
if (this.closed) return this;
const paths = unifyPaths(paths_);
const { cwd } = this.options;
paths.forEach((path$2) => {
if (!sysPath.isAbsolute(path$2) && !this._closers.has(path$2)) {
if (cwd) path$2 = sysPath.join(cwd, path$2);
path$2 = sysPath.resolve(path$2);
}
this._closePath(path$2);
this._addIgnoredPath(path$2);
if (this._watched.has(path$2)) this._addIgnoredPath({
path: path$2,
recursive: true
});
this._userIgnored = void 0;
});
return this;
}
/**
* Close watchers and remove all listeners from watched paths.
*/
close() {
if (this._closePromise) return this._closePromise;
this.closed = true;
this.removeAllListeners();
const closers = [];
this._closers.forEach((closerList) => closerList.forEach((closer) => {
const promise = closer();
if (promise instanceof Promise) closers.push(promise);
}));
this._streams.forEach((stream) => stream.destroy());
this._userIgnored = void 0;
this._readyCount = 0;
this._readyEmitted = false;
this._watched.forEach((dirent) => dirent.dispose());
this._closers.clear();
this._watched.clear();
this._streams.clear();
this._symlinkPaths.clear();
this._throttled.clear();
this._closePromise = closers.length ? Promise.all(closers).then(() => void 0) : Promise.resolve();
return this._closePromise;
}
/**
* Expose list of watched paths
* @returns for chaining
*/
getWatched() {
const watchList = {};
this._watched.forEach((entry, dir) => {
const index = (this.options.cwd ? sysPath.relative(this.options.cwd, dir) : dir) || ONE_DOT;
watchList[index] = entry.getChildren().sort();
});
return watchList;
}
emitWithAll(event, args) {
this.emit(event, ...args);
if (event !== EVENTS.ERROR) this.emit(EVENTS.ALL, event, ...args);
}
/**
* Normalize and emit events.
* Calling _emit DOES NOT MEAN emit() would be called!
* @param event Type of event
* @param path File or directory path
* @param stats arguments to be passed with event
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
*/
async _emit(event, path$2, stats) {
if (this.closed) return;
const opts = this.options;
if (isWindows) path$2 = sysPath.normalize(path$2);
if (opts.cwd) path$2 = sysPath.relative(opts.cwd, path$2);
const args = [path$2];
if (stats != null) args.push(stats);
const awf = opts.awaitWriteFinish;
let pw;
if (awf && (pw = this._pendingWrites.get(path$2))) {
pw.lastChange = /* @__PURE__ */ new Date();
return this;
}
if (opts.atomic) {
if (event === EVENTS.UNLINK) {
this._pendingUnlinks.set(path$2, [event, ...args]);
setTimeout(() => {
this._pendingUnlinks.forEach((entry, path$3) => {
this.emit(...entry);
this.emit(EVENTS.ALL, ...entry);
this._pendingUnlinks.delete(path$3);
});
}, typeof opts.atomic === "number" ? opts.atomic : 100);
return this;
}
if (event === EVENTS.ADD && this._pendingUnlinks.has(path$2)) {
event = EVENTS.CHANGE;
this._pendingUnlinks.delete(path$2);
}
}
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
const awfEmit = (err, stats$1) => {
if (err) {
event = EVENTS.ERROR;
args[0] = err;
this.emitWithAll(event, args);
} else if (stats$1) {
if (args.length > 1) args[1] = stats$1;
else args.push(stats$1);
this.emitWithAll(event, args);
}
};
this._awaitWriteFinish(path$2, awf.stabilityThreshold, event, awfEmit);
return this;
}
if (event === EVENTS.CHANGE) {
if (!this._throttle(EVENTS.CHANGE, path$2, 50)) return this;
}
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
const fullPath = opts.cwd ? sysPath.join(opts.cwd, path$2) : path$2;
let stats$1;
try {
stats$1 = await stat(fullPath);
} catch (err) {}
if (!stats$1 || this.closed) return;
args.push(stats$1);
}
this.emitWithAll(event, args);
return this;
}
/**
* Common handler for errors
* @returns The error if defined, otherwise the value of the FSWatcher instance's `closed` flag
*/
_handleError(error) {
const code = error && error.code;
if (error && code !== "ENOENT" && code !== "ENOTDIR" && (!this.options.ignorePermissionErrors || code !== "EPERM" && code !== "EACCES")) this.emit(EVENTS.ERROR, error);
return error || this.closed;
}
/**
* Helper utility for throttling
* @param actionType type being throttled
* @param path being acted upon
* @param timeout duration of time to suppress duplicate actions
* @returns tracking object or false if action should be suppressed
*/
_throttle(actionType, path$2, timeout) {
if (!this._throttled.has(actionType)) this._throttled.set(actionType, /* @__PURE__ */ new Map());
const action = this._throttled.get(actionType);
if (!action) throw new Error("invalid throttle");
const actionPath = action.get(path$2);
if (actionPath) {
actionPath.count++;
return false;
}
let timeoutObject;
const clear = () => {
const item = action.get(path$2);
const count = item ? item.count : 0;
action.delete(path$2);
clearTimeout(timeoutObject);
if (item) clearTimeout(item.timeoutObject);
return count;
};
timeoutObject = setTimeout(clear, timeout);
const thr = {
timeoutObject,
clear,
count: 0
};
action.set(path$2, thr);
return thr;
}
_incrReadyCount() {
return this._readyCount++;
}
/**
* Awaits write operation to finish.
* Polls a newly created file for size variations. When files size does not change for 'threshold' milliseconds calls callback.
* @param path being acted upon
* @param threshold Time in milliseconds a file size must be fixed before acknowledging write OP is finished
* @param event
* @param awfEmit Callback to be called when ready for event to be emitted.
*/
_awaitWriteFinish(path$2, threshold, event, awfEmit) {
const awf = this.options.awaitWriteFinish;
if (typeof awf !== "object") return;
const pollInterval = awf.pollInterval;
let timeoutHandler;
let fullPath = path$2;
if (this.options.cwd && !sysPath.isAbsolute(path$2)) fullPath = sysPath.join(this.options.cwd, path$2);
const now = /* @__PURE__ */ new Date();
const writes = this._pendingWrites;
function awaitWriteFinishFn(prevStat) {
stat$1(fullPath, (err, curStat) => {
if (err || !writes.has(path$2)) {
if (err && err.code !== "ENOENT") awfEmit(err);
return;
}
const now$1 = Number(/* @__PURE__ */ new Date());
if (prevStat && curStat.size !== prevStat.size) writes.get(path$2).lastChange = now$1;
if (now$1 - writes.get(path$2).lastChange >= threshold) {
writes.delete(path$2);
awfEmit(void 0, curStat);
} else timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
});
}
if (!writes.has(path$2)) {
writes.set(path$2, {
lastChange: now,
cancelWait: () => {
writes.delete(path$2);
clearTimeout(timeoutHandler);
return event;
}
});
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval);
}
}
/**
* Determines whether user has asked to ignore this path.
*/
_isIgnored(path$2, stats) {
if (this.options.atomic && DOT_RE.test(path$2)) return true;
if (!this._userIgnored) {
const { cwd } = this.options;
const ignored = (this.options.ignored || []).map(normalizeIgnored(cwd));
this._userIgnored = anymatch([...[...this._ignoredPaths].map(normalizeIgnored(cwd)), ...ignored], void 0);
}
return this._userIgnored(path$2, stats);
}
_isntIgnored(path$2, stat$3) {
return !this._isIgnored(path$2, stat$3);
}
/**
* Provides a set of common helpers and properties relating to symlink handling.
* @param path file or directory pattern being watched
*/
_getWatchHelpers(path$2) {
return new WatchHelper(path$2, this.options.followSymlinks, this);
}
/**
* Provides directory tracking objects
* @param directory path of the directory
*/
_getWatchedDir(directory) {
const dir = sysPath.resolve(directory);
if (!this._watched.has(dir)) this._watched.set(dir, new DirEntry(dir, this._boundRemove));
return this._watched.get(dir);
}
/**
* Check for read permissions: https://stackoverflow.com/a/11781404/1358405
*/
_hasReadPermissions(stats) {
if (this.options.ignorePermissionErrors) return true;
return Boolean(Number(stats.mode) & 256);
}
/**
* Handles emitting unlink events for
* files and directories, and via recursion, for
* files and directories within directories that are unlinked
* @param directory within which the following item is located
* @param item base path of item/directory
*/
_remove(directory, item, isDirectory) {
const path$2 = sysPath.join(directory, item);
const fullPath = sysPath.resolve(path$2);
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path$2) || this._watched.has(fullPath);
if (!this._throttle("remove", path$2, 100)) return;
if (!isDirectory && this._watched.size === 1) this.add(directory, item, true);
this._getWatchedDir(path$2).getChildren().forEach((nested) => this._remove(path$2, nested));
const parent = this._getWatchedDir(directory);
const wasTracked = parent.has(item);
parent.remove(item);
if (this._symlinkPaths.has(fullPath)) this._symlinkPaths.delete(fullPath);
let relPath = path$2;
if (this.options.cwd) relPath = sysPath.relative(this.options.cwd, path$2);
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
if (this._pendingWrites.get(relPath).cancelWait() === EVENTS.ADD) return;
}
this._watched.delete(path$2);
this._watched.delete(fullPath);
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
if (wasTracked && !this._isIgnored(path$2)) this._emit(eventName, path$2);
this._closePath(path$2);
}
/**
* Closes all watchers for a path
*/
_closePath(path$2) {
this._closeFile(path$2);
const dir = sysPath.dirname(path$2);
this._getWatchedDir(dir).remove(sysPath.basename(path$2));
}
/**
* Closes only file-specific watchers
*/
_closeFile(path$2) {
const closers = this._closers.get(path$2);
if (!closers) return;
closers.forEach((closer) => closer());
this._closers.delete(path$2);
}
_addPathCloser(path$2, closer) {
if (!closer) return;
let list = this._closers.get(path$2);
if (!list) {
list = [];
this._closers.set(path$2, list);
}
list.push(closer);
}
_readdirp(root, opts) {
if (this.closed) return;
let stream = readdirp(root, {
type: EVENTS.ALL,
alwaysStat: true,
lstat: true,
...opts,
depth: 0
});
this._streams.add(stream);
stream.once(STR_CLOSE, () => {
stream = void 0;
});
stream.once(STR_END, () => {
if (stream) {
this._streams.delete(stream);
stream = void 0;
}
});
return stream;
}
};
/**
* Instantiates watcher with paths to be tracked.
* @param paths file / directory paths
* @param options opts, such as `atomic`, `awaitWriteFinish`, `ignored`, and others
* @returns an instance of FSWatcher for chaining.
* @example
* const watcher = watch('.').on('all', (event, path) => { console.log(event, path); });
* watch('.', { atomic: true, awaitWriteFinish: true, ignored: (f, stats) => stats?.isFile() && !f.endsWith('.js') })
*/
function watch$1(paths, options = {}) {
const watcher = new FSWatcher(options);
watcher.add(paths);
return watcher;
}
var esm_default = {
watch: watch$1,
FSWatcher
};
//#endregion
export { esm_default as t };