volume.js 65 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.FSWatcher = exports.StatWatcher = exports.Volume = void 0;
  4. exports.pathToSteps = pathToSteps;
  5. exports.dataToStr = dataToStr;
  6. exports.toUnixTimestamp = toUnixTimestamp;
  7. const pathModule = require("node:path");
  8. const core_1 = require("../core");
  9. const Stats_1 = require("./Stats");
  10. const Dirent_1 = require("./Dirent");
  11. const StatFs_1 = require("./StatFs");
  12. const buffer_1 = require("../internal/buffer");
  13. const queueMicrotask_1 = require("../queueMicrotask");
  14. const setTimeoutUnref_1 = require("../setTimeoutUnref");
  15. const node_stream_1 = require("node:stream");
  16. const constants_1 = require("../constants");
  17. const node_events_1 = require("node:events");
  18. const encoding_1 = require("../encoding");
  19. const FileHandle_1 = require("./FileHandle");
  20. const util = require("node:util");
  21. const FsPromises_1 = require("./FsPromises");
  22. const print_1 = require("../print");
  23. const constants_2 = require("./constants");
  24. const options_1 = require("./options");
  25. const util_1 = require("./util");
  26. const Dir_1 = require("./Dir");
  27. const util_2 = require("../core/util");
  28. const resolveCrossPlatform = pathModule.resolve;
  29. const { O_SYMLINK, F_OK, R_OK, W_OK, X_OK, COPYFILE_EXCL, COPYFILE_FICLONE_FORCE } = constants_1.constants;
  30. const { sep, relative, join, dirname, normalize } = pathModule.posix ? pathModule.posix : pathModule;
  31. // ---------------------------------------- Constants
  32. const kMinPoolSpace = 128;
  33. // ---------------------------------------- Utility functions
  34. function pathToSteps(path) {
  35. return (0, util_2.filenameToSteps)((0, util_1.pathToFilename)(path));
  36. }
  37. function dataToStr(data, encoding = encoding_1.ENCODING_UTF8) {
  38. if (buffer_1.Buffer.isBuffer(data))
  39. return data.toString(encoding);
  40. else if (data instanceof Uint8Array)
  41. return (0, buffer_1.bufferFrom)(data).toString(encoding);
  42. else
  43. return String(data);
  44. }
  45. // converts Date or number to a fractional UNIX timestamp
  46. function toUnixTimestamp(time) {
  47. // tslint:disable-next-line triple-equals
  48. if (typeof time === 'string' && +time == time) {
  49. return +time;
  50. }
  51. if (time instanceof Date) {
  52. return time.getTime() / 1000;
  53. }
  54. if (isFinite(time)) {
  55. if (time < 0) {
  56. return Date.now() / 1000;
  57. }
  58. return time;
  59. }
  60. throw new Error('Cannot parse time: ' + time);
  61. }
  62. function validateUid(uid) {
  63. if (typeof uid !== 'number')
  64. throw TypeError(constants_2.ERRSTR.UID);
  65. }
  66. function validateGid(gid) {
  67. if (typeof gid !== 'number')
  68. throw TypeError(constants_2.ERRSTR.GID);
  69. }
  70. /**
  71. * `Volume` represents a file system.
  72. */
  73. class Volume {
  74. get promises() {
  75. if (this.promisesApi === null)
  76. throw new Error('Promise is not supported in this environment.');
  77. return this.promisesApi;
  78. }
  79. constructor(_core = new core_1.Superblock()) {
  80. this._core = _core;
  81. this.promisesApi = new FsPromises_1.FsPromises(this, FileHandle_1.FileHandle);
  82. this.openSync = (path, flags, mode = 438 /* MODE.DEFAULT */) => {
  83. // Validate (1) mode; (2) path; (3) flags - in that order.
  84. const modeNum = (0, util_1.modeToNumber)(mode);
  85. const fileName = (0, util_1.pathToFilename)(path);
  86. const flagsNum = (0, util_1.flagsToNumber)(flags);
  87. return this._core.open(fileName, flagsNum, modeNum, !(flagsNum & O_SYMLINK));
  88. };
  89. this.open = (path, flags, a, b) => {
  90. let mode = a;
  91. let callback = b;
  92. if (typeof a === 'function') {
  93. mode = 438 /* MODE.DEFAULT */;
  94. callback = a;
  95. }
  96. mode = mode || 438 /* MODE.DEFAULT */;
  97. const modeNum = (0, util_1.modeToNumber)(mode);
  98. const fileName = (0, util_1.pathToFilename)(path);
  99. const flagsNum = (0, util_1.flagsToNumber)(flags);
  100. this.wrapAsync(this._core.open, [fileName, flagsNum, modeNum, !(flagsNum & O_SYMLINK)], callback);
  101. };
  102. this.closeSync = (fd) => {
  103. this._core.close(fd);
  104. };
  105. this.close = (fd, callback) => {
  106. (0, util_2.validateFd)(fd);
  107. const file = this._core.getFileByFdOrThrow(fd, 'close');
  108. this.wrapAsync(this._core.close, [file.fd], callback);
  109. };
  110. this.readSync = (fd, buffer, offset, length, position) => {
  111. (0, util_2.validateFd)(fd);
  112. return this._core.read(fd, buffer, offset, length, position);
  113. };
  114. this.read = (fd, buffer, offset, length, position, callback) => {
  115. (0, util_1.validateCallback)(callback);
  116. if (length === 0) {
  117. // This `if` branch is from Node.js
  118. return (0, queueMicrotask_1.default)(() => {
  119. if (callback)
  120. callback(null, 0, buffer);
  121. });
  122. }
  123. Promise.resolve().then(() => {
  124. try {
  125. const bytes = this._core.read(fd, buffer, offset, length, position);
  126. callback(null, bytes, buffer);
  127. }
  128. catch (err) {
  129. callback(err);
  130. }
  131. });
  132. };
  133. this.readv = (fd, buffers, a, b) => {
  134. let position = a;
  135. let callback = b;
  136. if (typeof a === 'function')
  137. [position, callback] = [null, a];
  138. (0, util_1.validateCallback)(callback);
  139. Promise.resolve().then(() => {
  140. try {
  141. const bytes = this._core.readv(fd, buffers, position);
  142. callback(null, bytes, buffers);
  143. }
  144. catch (err) {
  145. callback(err);
  146. }
  147. });
  148. };
  149. this.readvSync = (fd, buffers, position) => {
  150. (0, util_2.validateFd)(fd);
  151. return this._core.readv(fd, buffers, position ?? null);
  152. };
  153. this._readfile = (id, flagsNum, encoding) => {
  154. let result;
  155. const isUserFd = typeof id === 'number';
  156. const userOwnsFd = isUserFd && (0, util_2.isFd)(id);
  157. let fd;
  158. if (userOwnsFd)
  159. fd = id;
  160. else {
  161. const filename = (0, util_1.pathToFilename)(id);
  162. // Check if original path had trailing slash (indicates directory intent)
  163. const originalPath = String(id);
  164. const hasTrailingSlash = originalPath.length > 1 && originalPath.endsWith('/');
  165. const link = this._core.getResolvedLinkOrThrow(filename, 'open');
  166. const node = link.getNode();
  167. if (node.isDirectory())
  168. throw (0, util_1.createError)("EISDIR" /* ERROR_CODE.EISDIR */, 'open', link.getPath());
  169. // If path had trailing slash but resolved to a file, throw ENOTDIR
  170. if (hasTrailingSlash && node.isFile()) {
  171. throw (0, util_1.createError)("ENOTDIR" /* ERROR_CODE.ENOTDIR */, 'open', originalPath);
  172. }
  173. fd = this.openSync(id, flagsNum);
  174. }
  175. try {
  176. result = (0, util_1.bufferToEncoding)(this._core.getFileByFdOrThrow(fd).getBuffer(), encoding);
  177. }
  178. finally {
  179. if (!userOwnsFd) {
  180. this.closeSync(fd);
  181. }
  182. }
  183. return result;
  184. };
  185. this.readFileSync = (file, options) => {
  186. const opts = (0, options_1.getReadFileOptions)(options);
  187. const flagsNum = (0, util_1.flagsToNumber)(opts.flag);
  188. return this._readfile(file, flagsNum, opts.encoding);
  189. };
  190. this.readFile = (id, a, b) => {
  191. const [opts, callback] = (0, options_1.optsAndCbGenerator)(options_1.getReadFileOptions)(a, b);
  192. const flagsNum = (0, util_1.flagsToNumber)(opts.flag);
  193. this.wrapAsync(this._readfile, [id, flagsNum, opts.encoding], callback);
  194. };
  195. this.writeSync = (fd, a, b, c, d) => {
  196. const [, buf, offset, length, position] = (0, util_1.getWriteSyncArgs)(fd, a, b, c, d);
  197. return this._write(fd, buf, offset, length, position);
  198. };
  199. this.write = (fd, a, b, c, d, e) => {
  200. const [, asStr, buf, offset, length, position, cb] = (0, util_1.getWriteArgs)(fd, a, b, c, d, e);
  201. Promise.resolve().then(() => {
  202. try {
  203. const bytes = this._write(fd, buf, offset, length, position);
  204. if (!asStr) {
  205. cb(null, bytes, buf);
  206. }
  207. else {
  208. cb(null, bytes, a);
  209. }
  210. }
  211. catch (err) {
  212. cb(err);
  213. }
  214. });
  215. };
  216. this.writev = (fd, buffers, a, b) => {
  217. let position = a;
  218. let callback = b;
  219. if (typeof a === 'function')
  220. [position, callback] = [null, a];
  221. (0, util_1.validateCallback)(callback);
  222. Promise.resolve().then(() => {
  223. try {
  224. const bytes = this.writevBase(fd, buffers, position);
  225. callback(null, bytes, buffers);
  226. }
  227. catch (err) {
  228. callback(err);
  229. }
  230. });
  231. };
  232. this.writevSync = (fd, buffers, position) => {
  233. (0, util_2.validateFd)(fd);
  234. return this.writevBase(fd, buffers, position ?? null);
  235. };
  236. this.writeFileSync = (id, data, options) => {
  237. const opts = (0, options_1.getWriteFileOptions)(options);
  238. const flagsNum = (0, util_1.flagsToNumber)(opts.flag);
  239. const modeNum = (0, util_1.modeToNumber)(opts.mode);
  240. const buf = (0, util_2.dataToBuffer)(data, opts.encoding);
  241. this._core.writeFile(id, buf, flagsNum, modeNum);
  242. };
  243. this.writeFile = (id, data, a, b) => {
  244. let options = a;
  245. let callback = b;
  246. if (typeof a === 'function')
  247. [options, callback] = [options_1.writeFileDefaults, a];
  248. const cb = (0, util_1.validateCallback)(callback);
  249. const opts = (0, options_1.getWriteFileOptions)(options);
  250. const flagsNum = (0, util_1.flagsToNumber)(opts.flag);
  251. const modeNum = (0, util_1.modeToNumber)(opts.mode);
  252. const buf = (0, util_2.dataToBuffer)(data, opts.encoding);
  253. this.wrapAsync(this._core.writeFile, [id, buf, flagsNum, modeNum], cb);
  254. };
  255. this.copyFileSync = (src, dest, flags) => {
  256. const srcFilename = (0, util_1.pathToFilename)(src);
  257. const destFilename = (0, util_1.pathToFilename)(dest);
  258. return this._copyFile(srcFilename, destFilename, (flags || 0) | 0);
  259. };
  260. this.copyFile = (src, dest, a, b) => {
  261. const srcFilename = (0, util_1.pathToFilename)(src);
  262. const destFilename = (0, util_1.pathToFilename)(dest);
  263. let flags;
  264. let callback;
  265. if (typeof a === 'function')
  266. [flags, callback] = [0, a];
  267. else
  268. [flags, callback] = [a, b];
  269. (0, util_1.validateCallback)(callback);
  270. this.wrapAsync(this._copyFile, [srcFilename, destFilename, flags], callback);
  271. };
  272. this._cp = (src, dest, options) => {
  273. if (options.filter && !options.filter(src, dest))
  274. return;
  275. const srcStat = options.dereference ? this.statSync(src) : this.lstatSync(src);
  276. let destStat = null;
  277. try {
  278. destStat = this.lstatSync(dest);
  279. }
  280. catch (err) {
  281. if (err.code !== 'ENOENT') {
  282. throw err;
  283. }
  284. }
  285. // Check if src and dest are the same (both exist and have same inode)
  286. if (destStat && srcStat.ino === destStat.ino && srcStat.dev === destStat.dev)
  287. throw (0, util_1.createError)("EINVAL" /* ERROR_CODE.EINVAL */, 'cp', src, dest);
  288. // Check type compatibility
  289. if (destStat) {
  290. if (srcStat.isDirectory() && !destStat.isDirectory())
  291. throw (0, util_1.createError)("EISDIR" /* ERROR_CODE.EISDIR */, 'cp', src, dest);
  292. if (!srcStat.isDirectory() && destStat.isDirectory())
  293. throw (0, util_1.createError)("ENOTDIR" /* ERROR_CODE.ENOTDIR */, 'cp', src, dest);
  294. }
  295. // Check if trying to copy directory to subdirectory of itself
  296. if (srcStat.isDirectory() && this.isSrcSubdir(src, dest))
  297. throw (0, util_1.createError)("EINVAL" /* ERROR_CODE.EINVAL */, 'cp', src, dest);
  298. ENDURE_PARENT_DIR_EXISTS: {
  299. const parent = dirname(dest);
  300. if (!this.existsSync(parent))
  301. this.mkdirSync(parent, { recursive: true });
  302. }
  303. // Handle different file types
  304. if (srcStat.isDirectory()) {
  305. if (!options.recursive)
  306. throw (0, util_1.createError)("EISDIR" /* ERROR_CODE.EISDIR */, 'cp', src);
  307. this.cpDirSync(srcStat, destStat, src, dest, options);
  308. }
  309. else if (srcStat.isFile() || srcStat.isCharacterDevice() || srcStat.isBlockDevice()) {
  310. this.cpFileSync(srcStat, destStat, src, dest, options);
  311. }
  312. else if (srcStat.isSymbolicLink() && !options.dereference) {
  313. // Only handle as symlink if not dereferencing
  314. this.cpSymlinkSync(destStat, src, dest, options);
  315. }
  316. else {
  317. throw (0, util_1.createError)("EINVAL" /* ERROR_CODE.EINVAL */, 'cp', src);
  318. }
  319. };
  320. this.linkSync = (existingPath, newPath) => {
  321. const existingPathFilename = (0, util_1.pathToFilename)(existingPath);
  322. const newPathFilename = (0, util_1.pathToFilename)(newPath);
  323. this._core.link(existingPathFilename, newPathFilename);
  324. };
  325. this.link = (existingPath, newPath, callback) => {
  326. const existingPathFilename = (0, util_1.pathToFilename)(existingPath);
  327. const newPathFilename = (0, util_1.pathToFilename)(newPath);
  328. this.wrapAsync(this._core.link, [existingPathFilename, newPathFilename], callback);
  329. };
  330. this.unlinkSync = (path) => {
  331. const filename = (0, util_1.pathToFilename)(path);
  332. this._core.unlink(filename);
  333. };
  334. this.unlink = (path, callback) => {
  335. const filename = (0, util_1.pathToFilename)(path);
  336. this.wrapAsync(this._core.unlink, [filename], callback);
  337. };
  338. /**
  339. * `type` argument works only on Windows.
  340. * @param target
  341. * @param path
  342. * @param type
  343. */
  344. this.symlinkSync = (target, path, type) => {
  345. const targetFilename = (0, util_1.pathToFilename)(target);
  346. const pathFilename = (0, util_1.pathToFilename)(path);
  347. this._core.symlink(targetFilename, pathFilename);
  348. };
  349. this.symlink = (target, path, a, b) => {
  350. const callback = (0, util_1.validateCallback)(typeof a === 'function' ? a : b);
  351. const targetFilename = (0, util_1.pathToFilename)(target);
  352. const pathFilename = (0, util_1.pathToFilename)(path);
  353. this.wrapAsync(this._core.symlink, [targetFilename, pathFilename], callback);
  354. };
  355. this._lstat = (filename, bigint = false, throwIfNoEntry = false) => {
  356. let link;
  357. try {
  358. link = this._core.getLinkOrThrow(filename, 'lstat');
  359. }
  360. catch (err) {
  361. if (err.code === "ENOENT" /* ERROR_CODE.ENOENT */ && !throwIfNoEntry)
  362. return undefined;
  363. else
  364. throw err;
  365. }
  366. return Stats_1.default.build(link.getNode(), bigint);
  367. };
  368. this.lstatSync = (path, options) => {
  369. const { throwIfNoEntry = true, bigint = false } = (0, options_1.getStatOptions)(options);
  370. return this._lstat((0, util_1.pathToFilename)(path), bigint, throwIfNoEntry);
  371. };
  372. this.renameSync = (oldPath, newPath) => {
  373. const oldPathFilename = (0, util_1.pathToFilename)(oldPath);
  374. const newPathFilename = (0, util_1.pathToFilename)(newPath);
  375. this._core.rename(oldPathFilename, newPathFilename);
  376. };
  377. this.rename = (oldPath, newPath, callback) => {
  378. const oldPathFilename = (0, util_1.pathToFilename)(oldPath);
  379. const newPathFilename = (0, util_1.pathToFilename)(newPath);
  380. this.wrapAsync(this._core.rename, [oldPathFilename, newPathFilename], callback);
  381. };
  382. this.existsSync = (path) => {
  383. try {
  384. return this._exists((0, util_1.pathToFilename)(path));
  385. }
  386. catch (err) {
  387. return false;
  388. }
  389. };
  390. this.exists = (path, callback) => {
  391. const filename = (0, util_1.pathToFilename)(path);
  392. if (typeof callback !== 'function')
  393. throw Error(constants_2.ERRSTR.CB);
  394. Promise.resolve().then(() => {
  395. try {
  396. callback(this._exists(filename));
  397. }
  398. catch (err) {
  399. callback(false);
  400. }
  401. });
  402. };
  403. this.accessSync = (path, mode = F_OK) => {
  404. const filename = (0, util_1.pathToFilename)(path);
  405. mode = mode | 0;
  406. this._access(filename, mode);
  407. };
  408. this.access = (path, a, b) => {
  409. let mode = F_OK;
  410. let callback;
  411. if (typeof a !== 'function')
  412. [mode, callback] = [a | 0, (0, util_1.validateCallback)(b)];
  413. else
  414. callback = a;
  415. const filename = (0, util_1.pathToFilename)(path);
  416. this.wrapAsync(this._access, [filename, mode], callback);
  417. };
  418. this.appendFileSync = (id, data, options) => {
  419. const opts = (0, options_1.getAppendFileOpts)(options);
  420. // Force append behavior when using a supplied file descriptor.
  421. if (!opts.flag || (0, util_2.isFd)(id))
  422. opts.flag = 'a';
  423. this.writeFileSync(id, data, opts);
  424. };
  425. this.appendFile = (id, data, a, b) => {
  426. const [opts, callback] = (0, options_1.getAppendFileOptsAndCb)(a, b);
  427. // Force append behavior when using a supplied file descriptor.
  428. if (!opts.flag || (0, util_2.isFd)(id))
  429. opts.flag = 'a';
  430. this.writeFile(id, data, opts, callback);
  431. };
  432. this._readdir = (filename, options) => {
  433. const steps = (0, util_2.filenameToSteps)(filename);
  434. const link = this._core.getResolvedLinkOrThrow(filename, 'scandir');
  435. const node = link.getNode();
  436. if (!node.isDirectory())
  437. throw (0, util_1.createError)("ENOTDIR" /* ERROR_CODE.ENOTDIR */, 'scandir', filename);
  438. // Check we have permissions
  439. if (!node.canRead())
  440. throw (0, util_1.createError)("EACCES" /* ERROR_CODE.EACCES */, 'scandir', filename);
  441. const list = []; // output list
  442. for (const name of link.children.keys()) {
  443. const child = link.getChild(name);
  444. if (!child || name === '.' || name === '..')
  445. continue;
  446. list.push(Dirent_1.default.build(child, options.encoding));
  447. // recursion
  448. if (options.recursive && child.children.size) {
  449. const recurseOptions = { ...options, recursive: true, withFileTypes: true };
  450. const childList = this._readdir(child.getPath(), recurseOptions);
  451. list.push(...childList);
  452. }
  453. }
  454. if (!util_2.isWin && options.encoding !== 'buffer')
  455. list.sort((a, b) => {
  456. if (a.name < b.name)
  457. return -1;
  458. if (a.name > b.name)
  459. return 1;
  460. return 0;
  461. });
  462. if (options.withFileTypes)
  463. return list;
  464. let filename2 = filename;
  465. if (util_2.isWin)
  466. filename2 = filename2.replace(/\\/g, '/');
  467. return list.map(dirent => {
  468. if (options.recursive) {
  469. let fullPath = pathModule.join(dirent.parentPath, dirent.name.toString());
  470. if (util_2.isWin) {
  471. fullPath = fullPath.replace(/\\/g, '/');
  472. }
  473. return fullPath.replace(filename2 + pathModule.posix.sep, '');
  474. }
  475. return dirent.name;
  476. });
  477. };
  478. this.readdirSync = (path, options) => {
  479. const opts = (0, options_1.getReaddirOptions)(options);
  480. const filename = (0, util_1.pathToFilename)(path);
  481. return this._readdir(filename, opts);
  482. };
  483. this.readdir = (path, a, b) => {
  484. const [options, callback] = (0, options_1.getReaddirOptsAndCb)(a, b);
  485. const filename = (0, util_1.pathToFilename)(path);
  486. this.wrapAsync(this._readdir, [filename, options], callback);
  487. };
  488. this._readlink = (filename, encoding) => {
  489. const link = this._core.getLinkOrThrow(filename, 'readlink');
  490. const node = link.getNode();
  491. if (!node.isSymlink())
  492. throw (0, util_1.createError)("EINVAL" /* ERROR_CODE.EINVAL */, 'readlink', filename);
  493. return (0, encoding_1.strToEncoding)(node.symlink, encoding);
  494. };
  495. this.readlinkSync = (path, options) => {
  496. const opts = (0, options_1.getDefaultOpts)(options);
  497. const filename = (0, util_1.pathToFilename)(path);
  498. return this._readlink(filename, opts.encoding);
  499. };
  500. this.readlink = (path, a, b) => {
  501. const [opts, callback] = (0, options_1.getDefaultOptsAndCb)(a, b);
  502. const filename = (0, util_1.pathToFilename)(path);
  503. this.wrapAsync(this._readlink, [filename, opts.encoding], callback);
  504. };
  505. this._fsync = (fd) => {
  506. this._core.getFileByFdOrThrow(fd, 'fsync');
  507. };
  508. this.fsyncSync = (fd) => {
  509. this._fsync(fd);
  510. };
  511. this.fsync = (fd, callback) => {
  512. this.wrapAsync(this._fsync, [fd], callback);
  513. };
  514. this._fdatasync = (fd) => {
  515. this._core.getFileByFdOrThrow(fd, 'fdatasync');
  516. };
  517. this.fdatasyncSync = (fd) => {
  518. this._fdatasync(fd);
  519. };
  520. this.fdatasync = (fd, callback) => {
  521. this.wrapAsync(this._fdatasync, [fd], callback);
  522. };
  523. this._ftruncate = (fd, len) => {
  524. const file = this._core.getFileByFdOrThrow(fd, 'ftruncate');
  525. file.truncate(len);
  526. };
  527. this.ftruncateSync = (fd, len) => {
  528. this._ftruncate(fd, len);
  529. };
  530. this.ftruncate = (fd, a, b) => {
  531. const len = typeof a === 'number' ? a : 0;
  532. const callback = (0, util_1.validateCallback)(typeof a === 'number' ? b : a);
  533. this.wrapAsync(this._ftruncate, [fd, len], callback);
  534. };
  535. this._truncate = (path, len) => {
  536. const fd = this.openSync(path, 'r+');
  537. try {
  538. this.ftruncateSync(fd, len);
  539. }
  540. finally {
  541. this.closeSync(fd);
  542. }
  543. };
  544. /**
  545. * `id` should be a file descriptor or a path. `id` as file descriptor will
  546. * not be supported soon.
  547. */
  548. this.truncateSync = (id, len) => {
  549. if ((0, util_2.isFd)(id))
  550. return this.ftruncateSync(id, len);
  551. this._truncate(id, len);
  552. };
  553. this.truncate = (id, a, b) => {
  554. const len = typeof a === 'number' ? a : 0;
  555. const callback = (0, util_1.validateCallback)(typeof a === 'number' ? b : a);
  556. if ((0, util_2.isFd)(id))
  557. return this.ftruncate(id, len, callback);
  558. this.wrapAsync(this._truncate, [id, len], callback);
  559. };
  560. this._futimes = (fd, atime, mtime) => {
  561. const file = this._core.getFileByFdOrThrow(fd, 'futimes');
  562. const node = file.node;
  563. node.atime = new Date(atime * 1000);
  564. node.mtime = new Date(mtime * 1000);
  565. };
  566. this.futimesSync = (fd, atime, mtime) => {
  567. this._futimes(fd, toUnixTimestamp(atime), toUnixTimestamp(mtime));
  568. };
  569. this.futimes = (fd, atime, mtime, callback) => {
  570. this.wrapAsync(this._futimes, [fd, toUnixTimestamp(atime), toUnixTimestamp(mtime)], callback);
  571. };
  572. this._utimes = (filename, atime, mtime, followSymlinks = true) => {
  573. const core = this._core;
  574. const link = followSymlinks
  575. ? core.getResolvedLinkOrThrow(filename, 'utimes')
  576. : core.getLinkOrThrow(filename, 'lutimes');
  577. const node = link.getNode();
  578. node.atime = new Date(atime * 1000);
  579. node.mtime = new Date(mtime * 1000);
  580. };
  581. this.utimesSync = (path, atime, mtime) => {
  582. this._utimes((0, util_1.pathToFilename)(path), toUnixTimestamp(atime), toUnixTimestamp(mtime), true);
  583. };
  584. this.utimes = (path, atime, mtime, callback) => {
  585. this.wrapAsync(this._utimes, [(0, util_1.pathToFilename)(path), toUnixTimestamp(atime), toUnixTimestamp(mtime), true], callback);
  586. };
  587. this.lutimesSync = (path, atime, mtime) => {
  588. this._utimes((0, util_1.pathToFilename)(path), toUnixTimestamp(atime), toUnixTimestamp(mtime), false);
  589. };
  590. this.lutimes = (path, atime, mtime, callback) => {
  591. this.wrapAsync(this._utimes, [(0, util_1.pathToFilename)(path), toUnixTimestamp(atime), toUnixTimestamp(mtime), false], callback);
  592. };
  593. this.mkdirSync = (path, options) => {
  594. const opts = (0, options_1.getMkdirOptions)(options);
  595. const modeNum = (0, util_1.modeToNumber)(opts.mode, 0o777);
  596. const filename = (0, util_1.pathToFilename)(path);
  597. if (opts.recursive)
  598. return this._core.mkdirp(filename, modeNum);
  599. this._core.mkdir(filename, modeNum);
  600. };
  601. this.mkdir = (path, a, b) => {
  602. const opts = (0, options_1.getMkdirOptions)(a);
  603. const callback = (0, util_1.validateCallback)(typeof a === 'function' ? a : b);
  604. const modeNum = (0, util_1.modeToNumber)(opts.mode, 0o777);
  605. const filename = (0, util_1.pathToFilename)(path);
  606. if (opts.recursive)
  607. this.wrapAsync(this._core.mkdirp, [filename, modeNum], callback);
  608. else
  609. this.wrapAsync(this._core.mkdir, [filename, modeNum], callback);
  610. };
  611. this._mkdtemp = (prefix, encoding, retry = 5) => {
  612. const filename = prefix + (0, util_1.genRndStr6)();
  613. try {
  614. this._core.mkdir(filename, 511 /* MODE.DIR */);
  615. return (0, encoding_1.strToEncoding)(filename, encoding);
  616. }
  617. catch (err) {
  618. if (err.code === "EEXIST" /* ERROR_CODE.EEXIST */) {
  619. if (retry > 1)
  620. return this._mkdtemp(prefix, encoding, retry - 1);
  621. else
  622. throw Error('Could not create temp dir.');
  623. }
  624. else
  625. throw err;
  626. }
  627. };
  628. this.mkdtempSync = (prefix, options) => {
  629. const { encoding } = (0, options_1.getDefaultOpts)(options);
  630. if (!prefix || typeof prefix !== 'string')
  631. throw new TypeError('filename prefix is required');
  632. (0, util_1.nullCheck)(prefix);
  633. return this._mkdtemp(prefix, encoding);
  634. };
  635. this.mkdtemp = (prefix, a, b) => {
  636. const [{ encoding }, callback] = (0, options_1.getDefaultOptsAndCb)(a, b);
  637. if (!prefix || typeof prefix !== 'string')
  638. throw new TypeError('filename prefix is required');
  639. if (!(0, util_1.nullCheck)(prefix))
  640. return;
  641. this.wrapAsync(this._mkdtemp, [prefix, encoding], callback);
  642. };
  643. this.rmdirSync = (path, options) => {
  644. const opts = (0, options_1.getRmdirOptions)(options);
  645. this._core.rmdir((0, util_1.pathToFilename)(path), opts.recursive);
  646. };
  647. this.rmdir = (path, a, b) => {
  648. const opts = (0, options_1.getRmdirOptions)(a);
  649. const callback = (0, util_1.validateCallback)(typeof a === 'function' ? a : b);
  650. this.wrapAsync(this._core.rmdir, [(0, util_1.pathToFilename)(path), opts.recursive], callback);
  651. };
  652. this.rmSync = (path, options) => {
  653. this._core.rm((0, util_1.pathToFilename)(path), options?.force, options?.recursive);
  654. };
  655. this.rm = (path, a, b) => {
  656. const [opts, callback] = (0, options_1.getRmOptsAndCb)(a, b);
  657. this.wrapAsync(this._core.rm, [(0, util_1.pathToFilename)(path), opts?.force, opts?.recursive], callback);
  658. };
  659. this._fchmod = (fd, modeNum) => {
  660. const file = this._core.getFileByFdOrThrow(fd, 'fchmod');
  661. file.chmod(modeNum);
  662. };
  663. this.fchmodSync = (fd, mode) => {
  664. this._fchmod(fd, (0, util_1.modeToNumber)(mode));
  665. };
  666. this.fchmod = (fd, mode, callback) => {
  667. this.wrapAsync(this._fchmod, [fd, (0, util_1.modeToNumber)(mode)], callback);
  668. };
  669. this._chmod = (filename, modeNum, followSymlinks = true) => {
  670. const link = followSymlinks
  671. ? this._core.getResolvedLinkOrThrow(filename, 'chmod')
  672. : this._core.getLinkOrThrow(filename, 'chmod');
  673. const node = link.getNode();
  674. node.chmod(modeNum);
  675. };
  676. this.chmodSync = (path, mode) => {
  677. const modeNum = (0, util_1.modeToNumber)(mode);
  678. const filename = (0, util_1.pathToFilename)(path);
  679. this._chmod(filename, modeNum, true);
  680. };
  681. this.chmod = (path, mode, callback) => {
  682. const modeNum = (0, util_1.modeToNumber)(mode);
  683. const filename = (0, util_1.pathToFilename)(path);
  684. this.wrapAsync(this._chmod, [filename, modeNum], callback);
  685. };
  686. this._lchmod = (filename, modeNum) => {
  687. this._chmod(filename, modeNum, false);
  688. };
  689. this.lchmodSync = (path, mode) => {
  690. const modeNum = (0, util_1.modeToNumber)(mode);
  691. const filename = (0, util_1.pathToFilename)(path);
  692. this._lchmod(filename, modeNum);
  693. };
  694. this.lchmod = (path, mode, callback) => {
  695. const modeNum = (0, util_1.modeToNumber)(mode);
  696. const filename = (0, util_1.pathToFilename)(path);
  697. this.wrapAsync(this._lchmod, [filename, modeNum], callback);
  698. };
  699. this._fchown = (fd, uid, gid) => {
  700. this._core.getFileByFdOrThrow(fd, 'fchown').chown(uid, gid);
  701. };
  702. this.fchownSync = (fd, uid, gid) => {
  703. validateUid(uid);
  704. validateGid(gid);
  705. this._fchown(fd, uid, gid);
  706. };
  707. this.fchown = (fd, uid, gid, callback) => {
  708. validateUid(uid);
  709. validateGid(gid);
  710. this.wrapAsync(this._fchown, [fd, uid, gid], callback);
  711. };
  712. this._chown = (filename, uid, gid) => {
  713. const link = this._core.getResolvedLinkOrThrow(filename, 'chown');
  714. const node = link.getNode();
  715. node.chown(uid, gid);
  716. };
  717. this.chownSync = (path, uid, gid) => {
  718. validateUid(uid);
  719. validateGid(gid);
  720. this._chown((0, util_1.pathToFilename)(path), uid, gid);
  721. };
  722. this.chown = (path, uid, gid, callback) => {
  723. validateUid(uid);
  724. validateGid(gid);
  725. this.wrapAsync(this._chown, [(0, util_1.pathToFilename)(path), uid, gid], callback);
  726. };
  727. this._lchown = (filename, uid, gid) => {
  728. this._core.getLinkOrThrow(filename, 'lchown').getNode().chown(uid, gid);
  729. };
  730. this.lchownSync = (path, uid, gid) => {
  731. validateUid(uid);
  732. validateGid(gid);
  733. this._lchown((0, util_1.pathToFilename)(path), uid, gid);
  734. };
  735. this.lchown = (path, uid, gid, callback) => {
  736. validateUid(uid);
  737. validateGid(gid);
  738. this.wrapAsync(this._lchown, [(0, util_1.pathToFilename)(path), uid, gid], callback);
  739. };
  740. this.statWatchers = {};
  741. this.cpSync = (src, dest, options) => {
  742. const srcFilename = (0, util_1.pathToFilename)(src);
  743. const destFilename = (0, util_1.pathToFilename)(dest);
  744. const opts_ = {
  745. dereference: options?.dereference ?? false,
  746. errorOnExist: options?.errorOnExist ?? false,
  747. filter: options?.filter,
  748. force: options?.force ?? true,
  749. mode: options?.mode ?? 0,
  750. preserveTimestamps: options?.preserveTimestamps ?? false,
  751. recursive: options?.recursive ?? false,
  752. verbatimSymlinks: options?.verbatimSymlinks ?? false,
  753. };
  754. return this._cp(srcFilename, destFilename, opts_);
  755. };
  756. this.cp = (src, dest, a, b) => {
  757. const srcFilename = (0, util_1.pathToFilename)(src);
  758. const destFilename = (0, util_1.pathToFilename)(dest);
  759. let options;
  760. let callback;
  761. if (typeof a === 'function')
  762. [options, callback] = [{}, a];
  763. else
  764. [options, callback] = [a || {}, b];
  765. (0, util_1.validateCallback)(callback);
  766. const opts_ = {
  767. dereference: options?.dereference ?? false,
  768. errorOnExist: options?.errorOnExist ?? false,
  769. filter: options?.filter,
  770. force: options?.force ?? true,
  771. mode: options?.mode ?? 0,
  772. preserveTimestamps: options?.preserveTimestamps ?? false,
  773. recursive: options?.recursive ?? false,
  774. verbatimSymlinks: options?.verbatimSymlinks ?? false,
  775. };
  776. this.wrapAsync(this._cp, [srcFilename, destFilename, opts_], callback);
  777. };
  778. this.openAsBlob = async (path, options) => {
  779. const filename = (0, util_1.pathToFilename)(path);
  780. const link = this._core.getResolvedLinkOrThrow(filename, 'open');
  781. const node = link.getNode();
  782. if (node.isDirectory())
  783. throw (0, util_1.createError)("EISDIR" /* ERROR_CODE.EISDIR */, 'open', link.getPath());
  784. const buffer = node.getBuffer();
  785. const type = options?.type || '';
  786. return new Blob([buffer], { type });
  787. };
  788. this.glob = (pattern, ...args) => {
  789. const [options, callback] = args.length === 1 ? [{}, args[0]] : [args[0], args[1]];
  790. this.wrapAsync(this._globSync, [pattern, options || {}], callback);
  791. };
  792. this.globSync = (pattern, options = {}) => {
  793. return this._globSync(pattern, options);
  794. };
  795. this._globSync = (pattern, options = {}) => {
  796. const { globSync } = require('./glob');
  797. return globSync(this, pattern, options);
  798. };
  799. this._opendir = (filename, options) => {
  800. const link = this._core.getResolvedLinkOrThrow(filename, 'scandir');
  801. const node = link.getNode();
  802. if (!node.isDirectory())
  803. throw (0, util_1.createError)("ENOTDIR" /* ERROR_CODE.ENOTDIR */, 'scandir', filename);
  804. return new Dir_1.Dir(link, options);
  805. };
  806. this.opendirSync = (path, options) => {
  807. const opts = (0, options_1.getOpendirOptions)(options);
  808. const filename = (0, util_1.pathToFilename)(path);
  809. return this._opendir(filename, opts);
  810. };
  811. this.opendir = (path, a, b) => {
  812. const [options, callback] = (0, options_1.getOpendirOptsAndCb)(a, b);
  813. const filename = (0, util_1.pathToFilename)(path);
  814. this.wrapAsync(this._opendir, [filename, options], callback);
  815. };
  816. const self = this; // tslint:disable-line no-this-assignment
  817. this.StatWatcher = class extends StatWatcher {
  818. constructor() {
  819. super(self);
  820. }
  821. };
  822. const _ReadStream = FsReadStream;
  823. this.ReadStream = class extends _ReadStream {
  824. constructor(...args) {
  825. super(self, ...args);
  826. }
  827. };
  828. const _WriteStream = FsWriteStream;
  829. this.WriteStream = class extends _WriteStream {
  830. constructor(...args) {
  831. super(self, ...args);
  832. }
  833. };
  834. this.FSWatcher = class extends FSWatcher {
  835. constructor() {
  836. super(self);
  837. }
  838. };
  839. const _realpath = (filename, encoding) => {
  840. const realLink = this._core.getResolvedLinkOrThrow(filename, 'realpath');
  841. return (0, encoding_1.strToEncoding)(realLink.getPath() || '/', encoding);
  842. };
  843. const realpathImpl = (path, a, b) => {
  844. const [opts, callback] = (0, options_1.getRealpathOptsAndCb)(a, b);
  845. const pathFilename = (0, util_1.pathToFilename)(path);
  846. self.wrapAsync(_realpath, [pathFilename, opts.encoding], callback);
  847. };
  848. const realpathSyncImpl = (path, options) => _realpath((0, util_1.pathToFilename)(path), (0, options_1.getRealpathOptions)(options).encoding);
  849. this.realpath = realpathImpl;
  850. this.realpath.native = realpathImpl;
  851. this.realpathSync = realpathSyncImpl;
  852. this.realpathSync.native = realpathSyncImpl;
  853. }
  854. wrapAsync(method, args, callback) {
  855. (0, util_1.validateCallback)(callback);
  856. Promise.resolve().then(() => {
  857. let result;
  858. try {
  859. result = method.apply(this, args);
  860. }
  861. catch (err) {
  862. callback(err);
  863. return;
  864. }
  865. callback(null, result);
  866. });
  867. }
  868. toTree(opts = { separator: sep }) {
  869. return (0, print_1.toTreeSync)(this, opts);
  870. }
  871. reset() {
  872. this._core.reset();
  873. }
  874. toJSON(paths, json = {}, isRelative = false, asBuffer = false) {
  875. return this._core.toJSON(paths, json, isRelative, asBuffer);
  876. }
  877. fromJSON(json, cwd) {
  878. return this._core.fromJSON(json, cwd);
  879. }
  880. fromNestedJSON(json, cwd) {
  881. return this._core.fromNestedJSON(json, cwd);
  882. }
  883. // Legacy interface
  884. mountSync(mountpoint, json) {
  885. this._core.fromJSON(json, mountpoint);
  886. }
  887. _write(fd, buf, offset, length, position) {
  888. const file = this._core.getFileByFdOrThrow(fd, 'write');
  889. if (file.node.isSymlink()) {
  890. throw (0, util_1.createError)("EBADF" /* ERROR_CODE.EBADF */, 'write', file.link.getPath());
  891. }
  892. return file.write(buf, offset, length, position === -1 || typeof position !== 'number' ? undefined : position);
  893. }
  894. writevBase(fd, buffers, position) {
  895. const file = this._core.getFileByFdOrThrow(fd);
  896. let p = position ?? undefined;
  897. if (p === -1) {
  898. p = undefined;
  899. }
  900. let bytesWritten = 0;
  901. for (const buffer of buffers) {
  902. const nodeBuf = buffer_1.Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength);
  903. const bytes = file.write(nodeBuf, 0, nodeBuf.byteLength, p);
  904. p = undefined;
  905. bytesWritten += bytes;
  906. if (bytes < nodeBuf.byteLength)
  907. break;
  908. }
  909. return bytesWritten;
  910. }
  911. _copyFile(src, dest, flags) {
  912. const buf = this.readFileSync(src);
  913. if (flags & COPYFILE_EXCL && this.existsSync(dest))
  914. throw (0, util_1.createError)("EEXIST" /* ERROR_CODE.EEXIST */, 'copyFile', src, dest);
  915. if (flags & COPYFILE_FICLONE_FORCE)
  916. throw (0, util_1.createError)("ENOSYS" /* ERROR_CODE.ENOSYS */, 'copyFile', src, dest);
  917. this._core.writeFile(dest, buf, constants_2.FLAGS.w, 438 /* MODE.DEFAULT */);
  918. }
  919. isSrcSubdir(src, dest) {
  920. try {
  921. const normalizedSrc = normalize(src.startsWith('/') ? src : '/' + src);
  922. const normalizedDest = normalize(dest.startsWith('/') ? dest : '/' + dest);
  923. if (normalizedSrc === normalizedDest)
  924. return true;
  925. // Check if dest is under src by using relative path
  926. // If dest is under src, the relative path from src to dest won't start with '..'
  927. const relativePath = relative(normalizedSrc, normalizedDest);
  928. // If relative path is empty or doesn't start with '..', dest is under src
  929. return relativePath === '' || (!relativePath.startsWith('..') && !pathModule.isAbsolute(relativePath));
  930. }
  931. catch (error) {
  932. // If path operations fail, assume it's safe (don't block the copy)
  933. return false;
  934. }
  935. }
  936. cpFileSync(srcStat, destStat, src, dest, options) {
  937. if (destStat) {
  938. if (options.errorOnExist)
  939. throw (0, util_1.createError)("EEXIST" /* ERROR_CODE.EEXIST */, 'cp', dest);
  940. if (!options.force)
  941. return;
  942. this.unlinkSync(dest);
  943. }
  944. // Copy the file
  945. this.copyFileSync(src, dest, options.mode);
  946. // Preserve timestamps if requested
  947. if (options.preserveTimestamps)
  948. this.utimesSync(dest, srcStat.atime, srcStat.mtime);
  949. // Set file mode
  950. this.chmodSync(dest, Number(srcStat.mode));
  951. }
  952. cpDirSync(srcStat, destStat, src, dest, options) {
  953. if (!destStat) {
  954. this.mkdirSync(dest);
  955. }
  956. // Read directory contents
  957. const entries = this.readdirSync(src);
  958. for (const entry of entries) {
  959. const srcItem = join(src, String(entry));
  960. const destItem = join(dest, String(entry));
  961. // Apply filter to each item
  962. if (options.filter && !options.filter(srcItem, destItem)) {
  963. continue;
  964. }
  965. this._cp(srcItem, destItem, options);
  966. }
  967. // Set directory mode
  968. this.chmodSync(dest, Number(srcStat.mode));
  969. }
  970. cpSymlinkSync(destStat, src, dest, options) {
  971. let linkTarget = String(this.readlinkSync(src));
  972. if (!options.verbatimSymlinks && !pathModule.isAbsolute(linkTarget))
  973. linkTarget = resolveCrossPlatform(dirname(src), linkTarget);
  974. if (destStat)
  975. this.unlinkSync(dest);
  976. this.symlinkSync(linkTarget, dest);
  977. }
  978. lstat(path, a, b) {
  979. const [{ throwIfNoEntry = true, bigint = false }, callback] = (0, options_1.getStatOptsAndCb)(a, b);
  980. this.wrapAsync(this._lstat, [(0, util_1.pathToFilename)(path), bigint, throwIfNoEntry], callback);
  981. }
  982. _stat(filename, bigint = false, throwIfNoEntry = true) {
  983. let link;
  984. try {
  985. link = this._core.getResolvedLinkOrThrow(filename, 'stat');
  986. }
  987. catch (err) {
  988. if (err.code === "ENOENT" /* ERROR_CODE.ENOENT */ && !throwIfNoEntry)
  989. return undefined;
  990. else
  991. throw err;
  992. }
  993. return Stats_1.default.build(link.getNode(), bigint);
  994. }
  995. statSync(path, options) {
  996. const { bigint = true, throwIfNoEntry = true } = (0, options_1.getStatOptions)(options);
  997. return this._stat((0, util_1.pathToFilename)(path), bigint, throwIfNoEntry);
  998. }
  999. stat(path, a, b) {
  1000. const [{ bigint = false, throwIfNoEntry = true }, callback] = (0, options_1.getStatOptsAndCb)(a, b);
  1001. this.wrapAsync(this._stat, [(0, util_1.pathToFilename)(path), bigint, throwIfNoEntry], callback);
  1002. }
  1003. fstatBase(fd, bigint = false) {
  1004. const file = this._core.getFileByFd(fd);
  1005. if (!file)
  1006. throw (0, util_1.createError)("EBADF" /* ERROR_CODE.EBADF */, 'fstat');
  1007. return Stats_1.default.build(file.node, bigint);
  1008. }
  1009. fstatSync(fd, options) {
  1010. return this.fstatBase(fd, (0, options_1.getStatOptions)(options).bigint);
  1011. }
  1012. fstat(fd, a, b) {
  1013. const [opts, callback] = (0, options_1.getStatOptsAndCb)(a, b);
  1014. this.wrapAsync(this.fstatBase, [fd, opts.bigint], callback);
  1015. }
  1016. _exists(filename) {
  1017. return !!this._stat(filename);
  1018. }
  1019. _access(filename, mode) {
  1020. const link = this._core.getLinkOrThrow(filename, 'access');
  1021. const node = link.getNode();
  1022. // F_OK (0) just checks for existence, which we already confirmed above
  1023. if (mode === F_OK) {
  1024. return;
  1025. }
  1026. // Check read permission
  1027. if (mode & R_OK && !node.canRead()) {
  1028. throw (0, util_1.createError)("EACCES" /* ERROR_CODE.EACCES */, 'access', filename);
  1029. }
  1030. // Check write permission
  1031. if (mode & W_OK && !node.canWrite()) {
  1032. throw (0, util_1.createError)("EACCES" /* ERROR_CODE.EACCES */, 'access', filename);
  1033. }
  1034. // Check execute permission
  1035. if (mode & X_OK && !node.canExecute()) {
  1036. throw (0, util_1.createError)("EACCES" /* ERROR_CODE.EACCES */, 'access', filename);
  1037. }
  1038. }
  1039. watchFile(path, a, b) {
  1040. const filename = (0, util_1.pathToFilename)(path);
  1041. let options = a;
  1042. let listener = b;
  1043. if (typeof options === 'function') {
  1044. listener = a;
  1045. options = null;
  1046. }
  1047. if (typeof listener !== 'function') {
  1048. throw Error('"watchFile()" requires a listener function');
  1049. }
  1050. let interval = 5007;
  1051. let persistent = true;
  1052. if (options && typeof options === 'object') {
  1053. if (typeof options.interval === 'number')
  1054. interval = options.interval;
  1055. if (typeof options.persistent === 'boolean')
  1056. persistent = options.persistent;
  1057. }
  1058. let watcher = this.statWatchers[filename];
  1059. if (!watcher) {
  1060. watcher = new this.StatWatcher();
  1061. watcher.start(filename, persistent, interval);
  1062. this.statWatchers[filename] = watcher;
  1063. }
  1064. watcher.addListener('change', listener);
  1065. return watcher;
  1066. }
  1067. unwatchFile(path, listener) {
  1068. const filename = (0, util_1.pathToFilename)(path);
  1069. const watcher = this.statWatchers[filename];
  1070. if (!watcher)
  1071. return;
  1072. if (typeof listener === 'function') {
  1073. watcher.removeListener('change', listener);
  1074. }
  1075. else {
  1076. watcher.removeAllListeners('change');
  1077. }
  1078. if (watcher.listenerCount('change') === 0) {
  1079. watcher.stop();
  1080. delete this.statWatchers[filename];
  1081. }
  1082. }
  1083. createReadStream(path, options) {
  1084. return new this.ReadStream(path, options);
  1085. }
  1086. createWriteStream(path, options) {
  1087. return new this.WriteStream(path, options);
  1088. }
  1089. // watch(path: PathLike): FSWatcher;
  1090. // watch(path: PathLike, options?: IWatchOptions | string): FSWatcher;
  1091. watch(path, options, listener) {
  1092. const filename = (0, util_1.pathToFilename)(path);
  1093. let givenOptions = options;
  1094. if (typeof options === 'function') {
  1095. listener = options;
  1096. givenOptions = null;
  1097. }
  1098. // tslint:disable-next-line prefer-const
  1099. let { persistent, recursive, encoding } = (0, options_1.getDefaultOpts)(givenOptions);
  1100. if (persistent === undefined)
  1101. persistent = true;
  1102. if (recursive === undefined)
  1103. recursive = false;
  1104. const watcher = new this.FSWatcher();
  1105. watcher.start(filename, persistent, recursive, encoding);
  1106. if (listener) {
  1107. watcher.addListener('change', listener);
  1108. }
  1109. return watcher;
  1110. }
  1111. _statfs(filename, bigint = false) {
  1112. // Verify the path exists to match Node.js behavior
  1113. this._core.getResolvedLinkOrThrow(filename, 'statfs');
  1114. return StatFs_1.default.build(this._core, bigint);
  1115. }
  1116. statfsSync(path, options) {
  1117. const { bigint = false } = (0, options_1.getStatfsOptions)(options);
  1118. return this._statfs((0, util_1.pathToFilename)(path), bigint);
  1119. }
  1120. statfs(path, a, b) {
  1121. const [{ bigint = false }, callback] = (0, options_1.getStatfsOptsAndCb)(a, b);
  1122. this.wrapAsync(this._statfs, [(0, util_1.pathToFilename)(path), bigint], callback);
  1123. }
  1124. }
  1125. exports.Volume = Volume;
  1126. Volume.fromJSON = (json, cwd) => new Volume(core_1.Superblock.fromJSON(json, cwd));
  1127. Volume.fromNestedJSON = (json, cwd) => new Volume(core_1.Superblock.fromNestedJSON(json, cwd));
  1128. function emitStop(self) {
  1129. self.emit('stop');
  1130. }
  1131. class StatWatcher extends node_events_1.EventEmitter {
  1132. constructor(vol) {
  1133. super();
  1134. this.onInterval = () => {
  1135. try {
  1136. const stats = this.vol.statSync(this.filename);
  1137. if (this.hasChanged(stats)) {
  1138. this.emit('change', stats, this.prev);
  1139. this.prev = stats;
  1140. }
  1141. }
  1142. finally {
  1143. this.loop();
  1144. }
  1145. };
  1146. this.vol = vol;
  1147. }
  1148. loop() {
  1149. this.timeoutRef = this.setTimeout(this.onInterval, this.interval);
  1150. }
  1151. hasChanged(stats) {
  1152. // if(!this.prev) return false;
  1153. if (stats.mtimeMs > this.prev.mtimeMs)
  1154. return true;
  1155. if (stats.nlink !== this.prev.nlink)
  1156. return true;
  1157. return false;
  1158. }
  1159. start(path, persistent = true, interval = 5007) {
  1160. this.filename = (0, util_1.pathToFilename)(path);
  1161. this.setTimeout = persistent
  1162. ? setTimeout.bind(typeof globalThis !== 'undefined' ? globalThis : global)
  1163. : setTimeoutUnref_1.default;
  1164. this.interval = interval;
  1165. this.prev = this.vol.statSync(this.filename);
  1166. this.loop();
  1167. }
  1168. stop() {
  1169. clearTimeout(this.timeoutRef);
  1170. (0, queueMicrotask_1.default)(() => {
  1171. emitStop.call(this, this);
  1172. });
  1173. }
  1174. }
  1175. exports.StatWatcher = StatWatcher;
  1176. /* tslint:disable no-var-keyword prefer-const */
  1177. // ---------------------------------------- ReadStream
  1178. var pool;
  1179. function allocNewPool(poolSize) {
  1180. pool = (0, buffer_1.bufferAllocUnsafe)(poolSize);
  1181. pool.used = 0;
  1182. }
  1183. util.inherits(FsReadStream, node_stream_1.Readable);
  1184. exports.ReadStream = FsReadStream;
  1185. function FsReadStream(vol, path, options) {
  1186. if (!(this instanceof FsReadStream))
  1187. return new FsReadStream(vol, path, options);
  1188. this._vol = vol;
  1189. // a little bit bigger buffer and water marks by default
  1190. options = Object.assign({}, (0, options_1.getOptions)(options, {}));
  1191. if (options.highWaterMark === undefined)
  1192. options.highWaterMark = 64 * 1024;
  1193. node_stream_1.Readable.call(this, options);
  1194. this.path = (0, util_1.pathToFilename)(path);
  1195. this.fd = options.fd === undefined ? null : typeof options.fd !== 'number' ? options.fd.fd : options.fd;
  1196. this.flags = options.flags === undefined ? 'r' : options.flags;
  1197. this.mode = options.mode === undefined ? 0o666 : options.mode;
  1198. this.start = options.start;
  1199. this.end = options.end;
  1200. this.autoClose = options.autoClose === undefined ? true : options.autoClose;
  1201. this.pos = undefined;
  1202. this.bytesRead = 0;
  1203. if (this.start !== undefined) {
  1204. if (typeof this.start !== 'number') {
  1205. throw new TypeError('"start" option must be a Number');
  1206. }
  1207. if (this.end === undefined) {
  1208. this.end = Infinity;
  1209. }
  1210. else if (typeof this.end !== 'number') {
  1211. throw new TypeError('"end" option must be a Number');
  1212. }
  1213. if (this.start > this.end) {
  1214. throw new Error('"start" option must be <= "end" option');
  1215. }
  1216. this.pos = this.start;
  1217. }
  1218. if (typeof this.fd !== 'number')
  1219. this.open();
  1220. this.on('end', function () {
  1221. if (this.autoClose) {
  1222. if (this.destroy)
  1223. this.destroy();
  1224. }
  1225. });
  1226. }
  1227. FsReadStream.prototype.open = function () {
  1228. var self = this; // tslint:disable-line no-this-assignment
  1229. this._vol.open(this.path, this.flags, this.mode, (er, fd) => {
  1230. if (er) {
  1231. if (self.autoClose) {
  1232. if (self.destroy)
  1233. self.destroy();
  1234. }
  1235. self.emit('error', er);
  1236. return;
  1237. }
  1238. self.fd = fd;
  1239. self.emit('open', fd);
  1240. // start the flow of data.
  1241. self.read();
  1242. });
  1243. };
  1244. FsReadStream.prototype._read = function (n) {
  1245. if (typeof this.fd !== 'number') {
  1246. return this.once('open', function () {
  1247. this._read(n);
  1248. });
  1249. }
  1250. if (this.destroyed)
  1251. return;
  1252. if (!pool || pool.length - pool.used < kMinPoolSpace) {
  1253. // discard the old pool.
  1254. allocNewPool(this._readableState.highWaterMark);
  1255. }
  1256. // Grab another reference to the pool in the case that while we're
  1257. // in the thread pool another read() finishes up the pool, and
  1258. // allocates a new one.
  1259. var thisPool = pool;
  1260. var toRead = Math.min(pool.length - pool.used, n);
  1261. var start = pool.used;
  1262. if (this.pos !== undefined)
  1263. toRead = Math.min(this.end - this.pos + 1, toRead);
  1264. // already read everything we were supposed to read!
  1265. // treat as EOF.
  1266. if (toRead <= 0)
  1267. return this.push(null);
  1268. // the actual read.
  1269. var self = this; // tslint:disable-line no-this-assignment
  1270. this._vol.read(this.fd, pool, pool.used, toRead, this.pos, onread);
  1271. // move the pool positions, and internal position for reading.
  1272. if (this.pos !== undefined)
  1273. this.pos += toRead;
  1274. pool.used += toRead;
  1275. function onread(er, bytesRead) {
  1276. if (er) {
  1277. if (self.autoClose && self.destroy) {
  1278. self.destroy();
  1279. }
  1280. self.emit('error', er);
  1281. }
  1282. else {
  1283. var b = null;
  1284. if (bytesRead > 0) {
  1285. self.bytesRead += bytesRead;
  1286. b = thisPool.slice(start, start + bytesRead);
  1287. }
  1288. self.push(b);
  1289. }
  1290. }
  1291. };
  1292. FsReadStream.prototype._destroy = function (err, cb) {
  1293. this.close(err2 => {
  1294. cb(err || err2);
  1295. });
  1296. };
  1297. FsReadStream.prototype.close = function (cb) {
  1298. if (cb)
  1299. this.once('close', cb);
  1300. if (this.closed || typeof this.fd !== 'number') {
  1301. if (typeof this.fd !== 'number') {
  1302. this.once('open', closeOnOpen);
  1303. return;
  1304. }
  1305. return (0, queueMicrotask_1.default)(() => this.emit('close'));
  1306. }
  1307. // Since Node 18, there is only a getter for '.closed'.
  1308. // The first branch mimics other setters from Readable.
  1309. // See https://github.com/nodejs/node/blob/v18.0.0/lib/internal/streams/readable.js#L1243
  1310. if (typeof this._readableState?.closed === 'boolean') {
  1311. this._readableState.closed = true;
  1312. }
  1313. else {
  1314. this.closed = true;
  1315. }
  1316. this._vol.close(this.fd, er => {
  1317. if (er)
  1318. this.emit('error', er);
  1319. else
  1320. this.emit('close');
  1321. });
  1322. this.fd = null;
  1323. };
  1324. // needed because as it will be called with arguments
  1325. // that does not match this.close() signature
  1326. function closeOnOpen(fd) {
  1327. this.close();
  1328. }
  1329. util.inherits(FsWriteStream, node_stream_1.Writable);
  1330. exports.WriteStream = FsWriteStream;
  1331. function FsWriteStream(vol, path, options) {
  1332. if (!(this instanceof FsWriteStream))
  1333. return new FsWriteStream(vol, path, options);
  1334. this._vol = vol;
  1335. options = Object.assign({}, (0, options_1.getOptions)(options, {}));
  1336. node_stream_1.Writable.call(this, options);
  1337. this.path = (0, util_1.pathToFilename)(path);
  1338. this.fd = options.fd === undefined ? null : typeof options.fd !== 'number' ? options.fd.fd : options.fd;
  1339. this.flags = options.flags === undefined ? 'w' : options.flags;
  1340. this.mode = options.mode === undefined ? 0o666 : options.mode;
  1341. this.start = options.start;
  1342. this.autoClose = options.autoClose === undefined ? true : !!options.autoClose;
  1343. this.pos = undefined;
  1344. this.bytesWritten = 0;
  1345. this.pending = true;
  1346. if (this.start !== undefined) {
  1347. if (typeof this.start !== 'number') {
  1348. throw new TypeError('"start" option must be a Number');
  1349. }
  1350. if (this.start < 0) {
  1351. throw new Error('"start" must be >= zero');
  1352. }
  1353. this.pos = this.start;
  1354. }
  1355. if (options.encoding)
  1356. this.setDefaultEncoding(options.encoding);
  1357. if (typeof this.fd !== 'number')
  1358. this.open();
  1359. // dispose on finish.
  1360. this.once('finish', function () {
  1361. if (this.autoClose) {
  1362. this.close();
  1363. }
  1364. });
  1365. }
  1366. FsWriteStream.prototype.open = function () {
  1367. this._vol.open(this.path, this.flags, this.mode, function (er, fd) {
  1368. if (er) {
  1369. if (this.autoClose && this.destroy) {
  1370. this.destroy();
  1371. }
  1372. this.emit('error', er);
  1373. return;
  1374. }
  1375. this.fd = fd;
  1376. this.pending = false;
  1377. this.emit('open', fd);
  1378. }.bind(this));
  1379. };
  1380. FsWriteStream.prototype._write = function (data, encoding, cb) {
  1381. if (!(data instanceof buffer_1.Buffer || data instanceof Uint8Array))
  1382. return this.emit('error', new Error('Invalid data'));
  1383. if (typeof this.fd !== 'number') {
  1384. return this.once('open', function () {
  1385. this._write(data, encoding, cb);
  1386. });
  1387. }
  1388. var self = this; // tslint:disable-line no-this-assignment
  1389. this._vol.write(this.fd, data, 0, data.length, this.pos, (er, bytes) => {
  1390. if (er) {
  1391. if (self.autoClose && self.destroy) {
  1392. self.destroy();
  1393. }
  1394. return cb(er);
  1395. }
  1396. self.bytesWritten += bytes;
  1397. cb();
  1398. });
  1399. if (this.pos !== undefined)
  1400. this.pos += data.length;
  1401. };
  1402. FsWriteStream.prototype._writev = function (data, cb) {
  1403. if (typeof this.fd !== 'number') {
  1404. return this.once('open', function () {
  1405. this._writev(data, cb);
  1406. });
  1407. }
  1408. const self = this; // tslint:disable-line no-this-assignment
  1409. const len = data.length;
  1410. const chunks = new Array(len);
  1411. var size = 0;
  1412. for (var i = 0; i < len; i++) {
  1413. var chunk = data[i].chunk;
  1414. chunks[i] = chunk;
  1415. size += chunk.length;
  1416. }
  1417. const buf = buffer_1.Buffer.concat(chunks);
  1418. this._vol.write(this.fd, buf, 0, buf.length, this.pos, (er, bytes) => {
  1419. if (er) {
  1420. if (self.destroy)
  1421. self.destroy();
  1422. return cb(er);
  1423. }
  1424. self.bytesWritten += bytes;
  1425. cb();
  1426. });
  1427. if (this.pos !== undefined)
  1428. this.pos += size;
  1429. };
  1430. FsWriteStream.prototype.close = function (cb) {
  1431. if (cb)
  1432. this.once('close', cb);
  1433. if (this.closed || typeof this.fd !== 'number') {
  1434. if (typeof this.fd !== 'number') {
  1435. this.once('open', closeOnOpen);
  1436. return;
  1437. }
  1438. return (0, queueMicrotask_1.default)(() => this.emit('close'));
  1439. }
  1440. // Since Node 18, there is only a getter for '.closed'.
  1441. // The first branch mimics other setters from Writable.
  1442. // See https://github.com/nodejs/node/blob/v18.0.0/lib/internal/streams/writable.js#L766
  1443. if (typeof this._writableState?.closed === 'boolean') {
  1444. this._writableState.closed = true;
  1445. }
  1446. else {
  1447. this.closed = true;
  1448. }
  1449. this._vol.close(this.fd, er => {
  1450. if (er)
  1451. this.emit('error', er);
  1452. else
  1453. this.emit('close');
  1454. });
  1455. this.fd = null;
  1456. };
  1457. FsWriteStream.prototype._destroy = FsReadStream.prototype._destroy;
  1458. // There is no shutdown() for files.
  1459. FsWriteStream.prototype.destroySoon = FsWriteStream.prototype.end;
  1460. // ---------------------------------------- FSWatcher
  1461. class FSWatcher extends node_events_1.EventEmitter {
  1462. constructor(vol) {
  1463. super();
  1464. this._filename = '';
  1465. this._filenameEncoded = '';
  1466. // _persistent: boolean = true;
  1467. this._recursive = false;
  1468. this._encoding = encoding_1.ENCODING_UTF8;
  1469. // inode -> removers
  1470. this._listenerRemovers = new Map();
  1471. this._onParentChild = (link) => {
  1472. if (link.getName() === this._getName()) {
  1473. this._emit('rename');
  1474. }
  1475. };
  1476. this._emit = (type) => {
  1477. this.emit('change', type, this._filenameEncoded);
  1478. };
  1479. this._persist = () => {
  1480. this._timer = setTimeout(this._persist, 1e6);
  1481. };
  1482. this._vol = vol;
  1483. // TODO: Emit "error" messages when watching.
  1484. // this._handle.onchange = function(status, eventType, filename) {
  1485. // if (status < 0) {
  1486. // self._handle.close();
  1487. // const error = !filename ?
  1488. // errnoException(status, 'Error watching file for changes:') :
  1489. // errnoException(status, `Error watching file ${filename} for changes:`);
  1490. // error.filename = filename;
  1491. // self.emit('error', error);
  1492. // } else {
  1493. // self.emit('change', eventType, filename);
  1494. // }
  1495. // };
  1496. }
  1497. _getName() {
  1498. return this._steps[this._steps.length - 1];
  1499. }
  1500. start(path, persistent = true, recursive = false, encoding = encoding_1.ENCODING_UTF8) {
  1501. this._filename = (0, util_1.pathToFilename)(path);
  1502. this._steps = (0, util_2.filenameToSteps)(this._filename);
  1503. this._filenameEncoded = (0, encoding_1.strToEncoding)(this._filename);
  1504. // this._persistent = persistent;
  1505. this._recursive = recursive;
  1506. this._encoding = encoding;
  1507. try {
  1508. this._link = this._vol._core.getLinkOrThrow(this._filename, 'FSWatcher');
  1509. }
  1510. catch (err) {
  1511. const error = new Error(`watch ${this._filename} ${err.code}`);
  1512. error.code = err.code;
  1513. error.errno = err.code;
  1514. throw error;
  1515. }
  1516. const watchLinkNodeChanged = (link) => {
  1517. const filepath = link.getPath();
  1518. const node = link.getNode();
  1519. const onNodeChange = () => {
  1520. let filename = relative(this._filename, filepath);
  1521. if (!filename)
  1522. filename = this._getName();
  1523. return this.emit('change', 'change', filename);
  1524. };
  1525. const unsub = node.changes.listen(([type]) => {
  1526. if (type === 'modify')
  1527. onNodeChange();
  1528. });
  1529. const removers = this._listenerRemovers.get(node.ino) ?? [];
  1530. removers.push(() => unsub());
  1531. this._listenerRemovers.set(node.ino, removers);
  1532. };
  1533. const watchLinkChildrenChanged = (link) => {
  1534. const node = link.getNode();
  1535. // when a new link added
  1536. const onLinkChildAdd = (l) => {
  1537. this.emit('change', 'rename', relative(this._filename, l.getPath()));
  1538. // 1. watch changes of the new link-node
  1539. watchLinkNodeChanged(l);
  1540. // 2. watch changes of the new link-node's children
  1541. watchLinkChildrenChanged(l);
  1542. };
  1543. // when a new link deleted
  1544. const onLinkChildDelete = (l) => {
  1545. // remove the listeners of the children nodes
  1546. const removeLinkNodeListeners = (curLink) => {
  1547. const ino = curLink.getNode().ino;
  1548. const removers = this._listenerRemovers.get(ino);
  1549. if (removers) {
  1550. removers.forEach(r => r());
  1551. this._listenerRemovers.delete(ino);
  1552. }
  1553. for (const [name, childLink] of curLink.children.entries()) {
  1554. if (childLink && name !== '.' && name !== '..') {
  1555. removeLinkNodeListeners(childLink);
  1556. }
  1557. }
  1558. };
  1559. removeLinkNodeListeners(l);
  1560. this.emit('change', 'rename', relative(this._filename, l.getPath()));
  1561. };
  1562. // children nodes changed
  1563. for (const [name, childLink] of link.children.entries()) {
  1564. if (childLink && name !== '.' && name !== '..') {
  1565. watchLinkNodeChanged(childLink);
  1566. }
  1567. }
  1568. // link children add/remove
  1569. const unsubscribeLinkChanges = link.changes.listen(([type, link]) => {
  1570. if (type === 'child:add')
  1571. onLinkChildAdd(link);
  1572. else if (type === 'child:del')
  1573. onLinkChildDelete(link);
  1574. });
  1575. const removers = this._listenerRemovers.get(node.ino) ?? [];
  1576. removers.push(() => {
  1577. unsubscribeLinkChanges();
  1578. });
  1579. if (recursive) {
  1580. for (const [name, childLink] of link.children.entries()) {
  1581. if (childLink && name !== '.' && name !== '..') {
  1582. watchLinkChildrenChanged(childLink);
  1583. }
  1584. }
  1585. }
  1586. };
  1587. watchLinkNodeChanged(this._link);
  1588. watchLinkChildrenChanged(this._link);
  1589. const parent = this._link.parent;
  1590. if (parent) {
  1591. // parent.on('child:delete', this._onParentChild);
  1592. parent.changes.listen(([type, link]) => {
  1593. if (type === 'child:del')
  1594. this._onParentChild(link);
  1595. });
  1596. }
  1597. if (persistent)
  1598. this._persist();
  1599. }
  1600. close() {
  1601. clearTimeout(this._timer);
  1602. this._listenerRemovers.forEach(removers => {
  1603. removers.forEach(r => r());
  1604. });
  1605. this._listenerRemovers.clear();
  1606. this._parentChangesUnsub?.();
  1607. }
  1608. }
  1609. exports.FSWatcher = FSWatcher;
  1610. //# sourceMappingURL=volume.js.map