FsaNodeFs.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.FsaNodeFs = void 0;
  4. const optHelpers = require("../node/options");
  5. const util = require("../node/util");
  6. const buffer_1 = require("../internal/buffer");
  7. const FsPromises_1 = require("../node/FsPromises");
  8. const util_1 = require("./util");
  9. const constants_1 = require("../node/constants");
  10. const encoding_1 = require("../encoding");
  11. const FsaNodeDirent_1 = require("./FsaNodeDirent");
  12. const constants_2 = require("../constants");
  13. const FsaNodeStats_1 = require("./FsaNodeStats");
  14. const queueMicrotask_1 = require("../queueMicrotask");
  15. const FsaNodeWriteStream_1 = require("./FsaNodeWriteStream");
  16. const FsaNodeReadStream_1 = require("./FsaNodeReadStream");
  17. const FsaNodeCore_1 = require("./FsaNodeCore");
  18. const FileHandle_1 = require("../node/FileHandle");
  19. const util_2 = require("../core/util");
  20. const notSupported = () => {
  21. throw new Error('Method not supported by the File System Access API.');
  22. };
  23. const notImplemented = () => {
  24. throw new Error('Not implemented');
  25. };
  26. const noop = () => { };
  27. /**
  28. * Constructs a Node.js `fs` API from a File System Access API
  29. * [`FileSystemDirectoryHandle` object](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle).
  30. */
  31. class FsaNodeFs extends FsaNodeCore_1.FsaNodeCore {
  32. constructor() {
  33. // ------------------------------------------------------------ FsPromisesApi
  34. super(...arguments);
  35. this.promises = new FsPromises_1.FsPromises(this, FileHandle_1.FileHandle);
  36. // ------------------------------------------------------------ FsCallbackApi
  37. this.open = (path, flags, a, b) => {
  38. let mode = a;
  39. let callback = b;
  40. if (typeof a === 'function') {
  41. mode = 438 /* MODE.DEFAULT */;
  42. callback = a;
  43. }
  44. mode = mode || 438 /* MODE.DEFAULT */;
  45. const modeNum = util.modeToNumber(mode);
  46. const filename = util.pathToFilename(path);
  47. const flagsNum = util.flagsToNumber(flags);
  48. this.__open(filename, flagsNum, modeNum).then(openFile => callback(null, openFile.fd), error => callback(error));
  49. };
  50. this.close = (fd, callback) => {
  51. (0, util_2.validateFd)(fd);
  52. this.__close(fd).then(() => callback(null), error => callback(error));
  53. };
  54. this.read = (fd, buffer, offset, length, position, callback) => {
  55. util.validateCallback(callback);
  56. // This `if` branch is from Node.js
  57. if (length === 0) {
  58. return (0, queueMicrotask_1.default)(() => {
  59. if (callback)
  60. callback(null, 0, buffer);
  61. });
  62. }
  63. (async () => {
  64. const openFile = await this.getFileByFd(fd, 'read');
  65. const file = await openFile.file.getFile();
  66. const src = await file.arrayBuffer();
  67. position = Number(position);
  68. length = Number(length);
  69. const slice = position > src.byteLength
  70. ? new Uint8Array(0)
  71. : new Uint8Array(src, position, Math.min(length, src.byteLength - position));
  72. const dest = new Uint8Array(buffer.buffer, buffer.byteOffset + offset, slice.length);
  73. dest.set(slice, 0);
  74. return slice.length;
  75. })().then(bytesWritten => callback(null, bytesWritten, buffer), error => callback(error));
  76. };
  77. this.readFile = (id, a, b) => {
  78. const [opts, callback] = optHelpers.optsAndCbGenerator(optHelpers.getReadFileOptions)(a, b);
  79. const flagsNum = util.flagsToNumber(opts.flag);
  80. (async () => {
  81. let fd = typeof id === 'number' ? id : -1;
  82. const originalFd = fd;
  83. try {
  84. if (fd === -1) {
  85. const filename = util.pathToFilename(id);
  86. fd = (await this.__open(filename, flagsNum, 0)).fd;
  87. }
  88. const handle = await this.__getFileById(fd, 'readFile');
  89. const file = await handle.getFile();
  90. const buffer = buffer_1.Buffer.from(await file.arrayBuffer());
  91. return util.bufferToEncoding(buffer, opts.encoding);
  92. }
  93. finally {
  94. try {
  95. const idWasFd = typeof originalFd === 'number' && originalFd >= 0;
  96. if (idWasFd)
  97. await this.__close(originalFd);
  98. }
  99. catch { }
  100. }
  101. })()
  102. .then(data => callback(null, data))
  103. .catch(error => callback(error));
  104. };
  105. this.write = (fd, a, b, c, d, e) => {
  106. const [, asStr, buf, offset, length, position, cb] = util.getWriteArgs(fd, a, b, c, d, e);
  107. (async () => {
  108. const openFile = await this.getFileByFd(fd, 'write');
  109. const data = buf.subarray(offset, offset + length);
  110. await openFile.write(data, position);
  111. return length;
  112. })().then(bytesWritten => cb(null, bytesWritten, asStr ? a : buf), error => cb(error));
  113. };
  114. this.writev = (fd, buffers, a, b) => {
  115. (0, util_2.validateFd)(fd);
  116. let position = null;
  117. let callback;
  118. if (typeof a === 'function') {
  119. callback = a;
  120. }
  121. else {
  122. position = Number(a);
  123. callback = b;
  124. }
  125. util.validateCallback(callback);
  126. (async () => {
  127. const openFile = await this.getFileByFd(fd, 'writev');
  128. const length = buffers.length;
  129. let bytesWritten = 0;
  130. for (let i = 0; i < length; i++) {
  131. const data = buffers[i];
  132. await openFile.write(data, position);
  133. bytesWritten += data.byteLength;
  134. position = null;
  135. }
  136. return bytesWritten;
  137. })().then(bytesWritten => callback(null, bytesWritten, buffers), error => callback(error));
  138. };
  139. this.writeFile = (id, data, a, b) => {
  140. let options = a;
  141. let callback = b;
  142. if (typeof a === 'function') {
  143. options = optHelpers.writeFileDefaults;
  144. callback = a;
  145. }
  146. const cb = util.validateCallback(callback);
  147. const opts = optHelpers.getWriteFileOptions(options);
  148. const flagsNum = util.flagsToNumber(opts.flag);
  149. const modeNum = util.modeToNumber(opts.mode);
  150. const buf = (0, util_2.dataToBuffer)(data, opts.encoding);
  151. (async () => {
  152. let fd = typeof id === 'number' ? id : -1;
  153. const originalFd = fd;
  154. try {
  155. if (fd === -1) {
  156. const filename = util.pathToFilename(id);
  157. fd = (await this.__open(filename, flagsNum, modeNum)).fd;
  158. }
  159. const file = await this.__getFileById(fd, 'writeFile');
  160. const writable = await file.createWritable({ keepExistingData: false });
  161. await writable.write(buf);
  162. await writable.close();
  163. }
  164. finally {
  165. try {
  166. const idWasFd = typeof originalFd === 'number' && originalFd >= 0;
  167. if (idWasFd)
  168. await this.__close(originalFd);
  169. }
  170. catch { }
  171. }
  172. })().then(() => cb(null), error => cb(error));
  173. };
  174. this.copyFile = (src, dest, a, b) => {
  175. const srcFilename = util.pathToFilename(src);
  176. const destFilename = util.pathToFilename(dest);
  177. let flags;
  178. let callback;
  179. if (typeof a === 'function') {
  180. flags = 0;
  181. callback = a;
  182. }
  183. else {
  184. flags = a;
  185. callback = b;
  186. }
  187. util.validateCallback(callback);
  188. const [oldFolder, oldName] = (0, util_1.pathToLocation)(srcFilename);
  189. const [newFolder, newName] = (0, util_1.pathToLocation)(destFilename);
  190. (async () => {
  191. const oldFile = await this.getFile(oldFolder, oldName, 'copyFile');
  192. const newDir = await this.getDir(newFolder, false, 'copyFile');
  193. const newFile = await newDir.getFileHandle(newName, { create: true });
  194. const writable = await newFile.createWritable({ keepExistingData: false });
  195. const oldData = await oldFile.getFile();
  196. await writable.write(await oldData.arrayBuffer());
  197. await writable.close();
  198. })().then(() => callback(null), error => callback(error));
  199. };
  200. /**
  201. * @todo There is a proposal for native "self remove" operation.
  202. * @see https://github.com/whatwg/fs/blob/main/proposals/Remove.md
  203. */
  204. this.unlink = (path, callback) => {
  205. const filename = util.pathToFilename(path);
  206. const [folder, name] = (0, util_1.pathToLocation)(filename);
  207. this.getDir(folder, false, 'unlink')
  208. .then(dir => dir.removeEntry(name))
  209. .then(() => callback(null), error => {
  210. if (error && typeof error === 'object') {
  211. switch (error.name) {
  212. case 'NotFoundError': {
  213. callback(util.createError('ENOENT', 'unlink', filename));
  214. return;
  215. }
  216. case 'InvalidModificationError': {
  217. callback(util.createError('EISDIR', 'unlink', filename));
  218. return;
  219. }
  220. }
  221. }
  222. callback(error);
  223. });
  224. };
  225. this.realpath = (path, a, b) => {
  226. const [opts, callback] = optHelpers.getRealpathOptsAndCb(a, b);
  227. let pathFilename = util.pathToFilename(path);
  228. if (pathFilename[0] !== "/" /* FsaToNodeConstants.Separator */)
  229. pathFilename = "/" /* FsaToNodeConstants.Separator */ + pathFilename;
  230. callback(null, (0, encoding_1.strToEncoding)(pathFilename, opts.encoding));
  231. };
  232. this.stat = (path, a, b) => {
  233. const [{ bigint = false, throwIfNoEntry = true }, callback] = optHelpers.getStatOptsAndCb(a, b);
  234. const filename = util.pathToFilename(path);
  235. const [folder, name] = (0, util_1.pathToLocation)(filename);
  236. (async () => {
  237. const handle = await this.getFileOrDir(folder, name, 'stat');
  238. return await this.getHandleStats(bigint, handle);
  239. })().then(stats => callback(null, stats), error => callback(error));
  240. };
  241. this.lstat = this.stat;
  242. this.fstat = (fd, a, b) => {
  243. const [{ bigint = false, throwIfNoEntry = true }, callback] = optHelpers.getStatOptsAndCb(a, b);
  244. (async () => {
  245. const openFile = await this.getFileByFd(fd, 'fstat');
  246. return await this.getHandleStats(bigint, openFile.file);
  247. })().then(stats => callback(null, stats), error => callback(error));
  248. };
  249. /**
  250. * @todo There is a proposal for native move support.
  251. * @see https://github.com/whatwg/fs/blob/main/proposals/MovingNonOpfsFiles.md
  252. */
  253. this.rename = (oldPath, newPath, callback) => {
  254. const oldPathFilename = util.pathToFilename(oldPath);
  255. const newPathFilename = util.pathToFilename(newPath);
  256. const [oldFolder, oldName] = (0, util_1.pathToLocation)(oldPathFilename);
  257. const [newFolder, newName] = (0, util_1.pathToLocation)(newPathFilename);
  258. (async () => {
  259. const oldFile = await this.getFile(oldFolder, oldName, 'rename');
  260. const newDir = await this.getDir(newFolder, false, 'rename');
  261. const newFile = await newDir.getFileHandle(newName, { create: true });
  262. const writable = await newFile.createWritable({ keepExistingData: false });
  263. const oldData = await oldFile.getFile();
  264. await writable.write(await oldData.arrayBuffer());
  265. await writable.close();
  266. const oldDir = await this.getDir(oldFolder, false, 'rename');
  267. await oldDir.removeEntry(oldName);
  268. })().then(() => callback(null), error => callback(error));
  269. };
  270. this.exists = (path, callback) => {
  271. const filename = util.pathToFilename(path);
  272. if (typeof callback !== 'function')
  273. throw Error(constants_1.ERRSTR.CB);
  274. this.access(path, 0 /* AMODE.F_OK */, error => callback(!error));
  275. };
  276. this.access = (path, a, b) => {
  277. let mode = 0 /* AMODE.F_OK */;
  278. let callback;
  279. if (typeof a !== 'function') {
  280. mode = a | 0; // cast to number
  281. callback = util.validateCallback(b);
  282. }
  283. else {
  284. callback = a;
  285. }
  286. const filename = util.pathToFilename(path);
  287. const [folder, name] = (0, util_1.pathToLocation)(filename);
  288. (async () => {
  289. const node = folder.length || name ? await this.getFileOrDir(folder, name, 'access') : await this.root;
  290. // Check execute permission - not supported by FSA
  291. const checkIfCanExecute = mode & 1 /* AMODE.X_OK */;
  292. if (checkIfCanExecute)
  293. throw util.createError('EACCESS', 'access', filename);
  294. // Use queryPermission to check read/write access
  295. const checkIfCanRead = mode & 4 /* AMODE.R_OK */;
  296. const checkIfCanWrite = mode & 2 /* AMODE.W_OK */;
  297. if (checkIfCanRead || checkIfCanWrite) {
  298. const permissionMode = checkIfCanWrite ? 'readwrite' : 'read';
  299. const permission = await node.queryPermission({ mode: permissionMode });
  300. if (permission.state === 'denied') {
  301. throw util.createError('EACCESS', 'access', filename);
  302. }
  303. }
  304. // If only F_OK is requested, we already verified the file exists by getting the node
  305. })().then(() => callback(null), error => callback(error));
  306. };
  307. this.appendFile = (id, data, a, b) => {
  308. const [opts, callback] = optHelpers.getAppendFileOptsAndCb(a, b);
  309. const buffer = (0, util_2.dataToBuffer)(data, opts.encoding);
  310. this.getFileByIdOrCreate(id, 'appendFile')
  311. .then(file => (async () => {
  312. const blob = await file.getFile();
  313. const writable = await file.createWritable({ keepExistingData: true });
  314. await writable.write({
  315. type: 'write',
  316. data: buffer,
  317. position: blob.size,
  318. });
  319. await writable.close();
  320. })())
  321. .then(() => callback(null), error => callback(error));
  322. };
  323. this.readdir = (path, a, b) => {
  324. const [options, callback] = optHelpers.getReaddirOptsAndCb(a, b);
  325. const filename = util.pathToFilename(path);
  326. const [folder, name] = (0, util_1.pathToLocation)(filename);
  327. if (name)
  328. folder.push(name);
  329. this.getDir(folder, false, 'readdir')
  330. .then(dir => (async () => {
  331. if (options.withFileTypes) {
  332. const list = [];
  333. for await (const [name, handle] of dir.entries()) {
  334. const dirent = new FsaNodeDirent_1.FsaNodeDirent(name, handle.kind);
  335. list.push(dirent);
  336. }
  337. if (!util_2.isWin && options.encoding !== 'buffer')
  338. list.sort((a, b) => {
  339. if (a.name < b.name)
  340. return -1;
  341. if (a.name > b.name)
  342. return 1;
  343. return 0;
  344. });
  345. return list;
  346. }
  347. else {
  348. const list = [];
  349. for await (const key of dir.keys())
  350. list.push(key);
  351. if (!util_2.isWin && options.encoding !== 'buffer')
  352. list.sort();
  353. return list;
  354. }
  355. })())
  356. .then(res => callback(null, res), err => callback(err));
  357. };
  358. this.readlink = (path, a, b) => {
  359. const [opts, callback] = optHelpers.getDefaultOptsAndCb(a, b);
  360. const filename = util.pathToFilename(path);
  361. const buffer = buffer_1.Buffer.from(filename);
  362. callback(null, util.bufferToEncoding(buffer, opts.encoding));
  363. };
  364. /** @todo Could this use `FileSystemSyncAccessHandle.flush` through a Worker thread? */
  365. this.fsync = (fd, callback) => {
  366. callback(null);
  367. };
  368. this.fdatasync = (fd, callback) => {
  369. callback(null);
  370. };
  371. this.ftruncate = (fd, a, b) => {
  372. const len = typeof a === 'number' ? a : 0;
  373. const callback = util.validateCallback(typeof a === 'number' ? b : a);
  374. this.getFileByFdAsync(fd)
  375. .then(file => file.file.createWritable({ keepExistingData: true }))
  376. .then(writable => writable.truncate(len).then(() => writable.close()))
  377. .then(() => callback(null), error => callback(error));
  378. };
  379. this.truncate = (path, a, b) => {
  380. const len = typeof a === 'number' ? a : 0;
  381. const callback = util.validateCallback(typeof a === 'number' ? b : a);
  382. this.open(path, 'r+', (error, fd) => {
  383. if (error)
  384. callback(error);
  385. else {
  386. this.ftruncate(fd, len, error => {
  387. if (error)
  388. this.close(fd, () => callback(error));
  389. else
  390. this.close(fd, callback);
  391. });
  392. }
  393. });
  394. };
  395. this.futimes = (fd, atime, mtime, callback) => {
  396. callback(null);
  397. };
  398. this.utimes = (path, atime, mtime, callback) => {
  399. callback(null);
  400. };
  401. this.mkdir = (path, a, b) => {
  402. const opts = optHelpers.getMkdirOptions(a);
  403. const callback = util.validateCallback(typeof a === 'function' ? a : b);
  404. // const modeNum = modeToNumber(opts.mode, 0o777);
  405. const filename = util.pathToFilename(path);
  406. const [folder, name] = (0, util_1.pathToLocation)(filename);
  407. // TODO: need to throw if directory already exists
  408. this.getDir(folder, opts.recursive ?? false)
  409. .then(dir => dir.getDirectoryHandle(name, { create: true }))
  410. .then(() => callback(null), error => {
  411. if (error && typeof error === 'object') {
  412. switch (error.name) {
  413. case 'NotFoundError': {
  414. const err = util.createError('ENOENT', 'mkdir', folder.join('/'));
  415. callback(err);
  416. return;
  417. }
  418. }
  419. }
  420. callback(error);
  421. });
  422. };
  423. this.mkdtemp = (prefix, a, b) => {
  424. const [{ encoding }, callback] = optHelpers.getDefaultOptsAndCb(a, b);
  425. if (!prefix || typeof prefix !== 'string')
  426. throw new TypeError('filename prefix is required');
  427. if (!util.nullCheck(prefix))
  428. return;
  429. const filename = prefix + util.genRndStr6();
  430. this.mkdir(filename, 511 /* MODE.DIR */, err => {
  431. if (err)
  432. callback(err);
  433. else
  434. callback(null, (0, encoding_1.strToEncoding)(filename, encoding));
  435. });
  436. };
  437. this.rmdir = (path, a, b) => {
  438. const options = optHelpers.getRmdirOptions(a);
  439. const callback = util.validateCallback(typeof a === 'function' ? a : b);
  440. const [folder, name] = (0, util_1.pathToLocation)(util.pathToFilename(path));
  441. if (!name && options.recursive)
  442. return this.rmAll(callback);
  443. this.getDir(folder, false, 'rmdir')
  444. .then(dir => dir.getDirectoryHandle(name).then(() => dir))
  445. .then(dir => dir.removeEntry(name, { recursive: options.recursive ?? false }))
  446. .then(() => callback(null), error => {
  447. if (error && typeof error === 'object') {
  448. switch (error.name) {
  449. case 'NotFoundError': {
  450. const err = util.createError('ENOENT', 'rmdir', folder.join('/'));
  451. callback(err);
  452. return;
  453. }
  454. case 'InvalidModificationError': {
  455. const err = util.createError('ENOTEMPTY', 'rmdir', folder.join('/'));
  456. callback(err);
  457. return;
  458. }
  459. }
  460. }
  461. callback(error);
  462. });
  463. };
  464. this.rm = (path, a, b) => {
  465. const [options, callback] = optHelpers.getRmOptsAndCb(a, b);
  466. const [folder, name] = (0, util_1.pathToLocation)(util.pathToFilename(path));
  467. if (!name && options.recursive)
  468. return this.rmAll(callback);
  469. this.getDir(folder, false, 'rmdir')
  470. .then(dir => dir.removeEntry(name, { recursive: options.recursive ?? false }))
  471. .then(() => callback(null), error => {
  472. if (options.force) {
  473. callback(null);
  474. return;
  475. }
  476. if (error && typeof error === 'object') {
  477. switch (error.name) {
  478. case 'NotFoundError': {
  479. const err = util.createError('ENOENT', 'rmdir', folder.join('/'));
  480. callback(err);
  481. return;
  482. }
  483. case 'InvalidModificationError': {
  484. const err = util.createError('ENOTEMPTY', 'rmdir', folder.join('/'));
  485. callback(err);
  486. return;
  487. }
  488. }
  489. }
  490. callback(error);
  491. });
  492. };
  493. this.fchmod = (fd, mode, callback) => {
  494. callback(null);
  495. };
  496. this.chmod = (path, mode, callback) => {
  497. callback(null);
  498. };
  499. this.lchmod = (path, mode, callback) => {
  500. callback(null);
  501. };
  502. this.fchown = (fd, uid, gid, callback) => {
  503. callback(null);
  504. };
  505. this.chown = (path, uid, gid, callback) => {
  506. callback(null);
  507. };
  508. this.lchown = (path, uid, gid, callback) => {
  509. callback(null);
  510. };
  511. this.createWriteStream = (path, options) => {
  512. const defaults = {
  513. encoding: 'utf8',
  514. flags: 'w',
  515. autoClose: true,
  516. emitClose: true,
  517. };
  518. const optionsObj = optHelpers.getOptions(defaults, options);
  519. const filename = util.pathToFilename(path);
  520. const flags = util.flagsToNumber(optionsObj.flags ?? 'w');
  521. const fd = optionsObj.fd ? (typeof optionsObj.fd === 'number' ? optionsObj.fd : optionsObj.fd.fd) : 0;
  522. const handle = fd ? this.getFileByFdAsync(fd) : this.__open(filename, flags, 0);
  523. const stream = new FsaNodeWriteStream_1.FsaNodeWriteStream(handle, filename, optionsObj);
  524. if (optionsObj.autoClose) {
  525. stream.once('finish', () => {
  526. handle.then(file => this.close(file.fd, () => { }));
  527. });
  528. stream.once('error', () => {
  529. handle.then(file => this.close(file.fd, () => { }));
  530. });
  531. }
  532. return stream;
  533. };
  534. this.createReadStream = (path, options) => {
  535. const defaults = {
  536. flags: 'r',
  537. fd: null,
  538. mode: 0o666,
  539. autoClose: true,
  540. emitClose: true,
  541. start: 0,
  542. end: Infinity,
  543. highWaterMark: 64 * 1024,
  544. fs: null,
  545. signal: null,
  546. };
  547. const optionsObj = optHelpers.getOptions(defaults, options);
  548. const filename = util.pathToFilename(path);
  549. const flags = util.flagsToNumber(optionsObj.flags);
  550. const fd = optionsObj.fd ? (typeof optionsObj.fd === 'number' ? optionsObj.fd : optionsObj.fd.fd) : 0;
  551. const handle = fd ? this.getFileByFdAsync(fd) : this.__open(filename, flags, 0);
  552. const stream = new FsaNodeReadStream_1.FsaNodeReadStream(this, handle, filename, optionsObj);
  553. return stream;
  554. };
  555. this.openAsBlob = async (path, options) => {
  556. const buffer = await new Promise((resolve, reject) => {
  557. this.readFile(path, (err, data) => {
  558. if (err)
  559. reject(err);
  560. else
  561. resolve(data);
  562. });
  563. });
  564. const type = options?.type || '';
  565. return new Blob([buffer], { type });
  566. };
  567. this.cp = notImplemented;
  568. this.lutimes = notImplemented;
  569. this.opendir = notImplemented;
  570. this.readv = notImplemented;
  571. this.statfs = notImplemented;
  572. this.glob = notImplemented;
  573. /**
  574. * @todo Implement using `FileSystemObserver` class.
  575. * @see https://developer.mozilla.org/en-US/docs/Web/API/FileSystemObserver
  576. */
  577. this.watchFile = notSupported;
  578. this.unwatchFile = notSupported;
  579. this.watch = notSupported;
  580. this.symlink = notSupported;
  581. this.link = notSupported;
  582. // --------------------------------------------------------- FsSynchronousApi
  583. this.statSync = (path, options) => {
  584. const { bigint = true, throwIfNoEntry = true } = optHelpers.getStatOptions(options);
  585. const filename = util.pathToFilename(path);
  586. const location = (0, util_1.pathToLocation)(filename);
  587. const adapter = this.getSyncAdapter();
  588. const res = adapter.call('stat', location);
  589. const stats = new FsaNodeStats_1.FsaNodeStats(bigint, res.size ?? 0, res.kind);
  590. return stats;
  591. };
  592. this.lstatSync = this.statSync;
  593. this.fstatSync = (fd, options) => {
  594. const filename = this.getFileName(fd);
  595. return this.statSync(filename, options);
  596. };
  597. this.accessSync = (path, mode = 0 /* AMODE.F_OK */) => {
  598. const filename = util.pathToFilename(path);
  599. mode = mode | 0;
  600. const adapter = this.getSyncAdapter();
  601. adapter.call('access', [filename, mode]);
  602. };
  603. this.readFileSync = (id, options) => {
  604. const opts = optHelpers.getReadFileOptions(options);
  605. const flagsNum = util.flagsToNumber(opts.flag);
  606. const filename = this.getFileName(id);
  607. const adapter = this.getSyncAdapter();
  608. const uint8 = adapter.call('readFile', [filename, opts]);
  609. const buffer = buffer_1.Buffer.from(uint8.buffer, uint8.byteOffset, uint8.byteLength);
  610. return util.bufferToEncoding(buffer, opts.encoding);
  611. };
  612. this.writeFileSync = (id, data, options) => {
  613. const opts = optHelpers.getWriteFileOptions(options);
  614. const flagsNum = util.flagsToNumber(opts.flag);
  615. const modeNum = util.modeToNumber(opts.mode);
  616. const buf = (0, util_2.dataToBuffer)(data, opts.encoding);
  617. const filename = this.getFileName(id);
  618. const adapter = this.getSyncAdapter();
  619. adapter.call('writeFile', [filename, util.bufToUint8(buf), opts]);
  620. };
  621. this.appendFileSync = (id, data, options) => {
  622. const opts = optHelpers.getAppendFileOpts(options);
  623. if (!opts.flag || (0, util_2.isFd)(id))
  624. opts.flag = 'a';
  625. const filename = this.getFileName(id);
  626. const buf = (0, util_2.dataToBuffer)(data, opts.encoding);
  627. const adapter = this.getSyncAdapter();
  628. adapter.call('appendFile', [filename, util.bufToUint8(buf), opts]);
  629. };
  630. this.closeSync = (fd) => {
  631. (0, util_2.validateFd)(fd);
  632. const file = this.getFileByFd(fd, 'close');
  633. file.close().catch(() => { });
  634. this.fds.delete(fd);
  635. this.releasedFds.push(fd);
  636. };
  637. this.existsSync = (path) => {
  638. try {
  639. this.statSync(path);
  640. return true;
  641. }
  642. catch {
  643. return false;
  644. }
  645. };
  646. this.copyFileSync = (src, dest, flags) => {
  647. const srcFilename = util.pathToFilename(src);
  648. const destFilename = util.pathToFilename(dest);
  649. const adapter = this.getSyncAdapter();
  650. adapter.call('copy', [srcFilename, destFilename, flags]);
  651. };
  652. this.renameSync = (oldPath, newPath) => {
  653. const srcFilename = util.pathToFilename(oldPath);
  654. const destFilename = util.pathToFilename(newPath);
  655. const adapter = this.getSyncAdapter();
  656. adapter.call('move', [srcFilename, destFilename]);
  657. };
  658. this.rmdirSync = (path, opts) => {
  659. const filename = util.pathToFilename(path);
  660. const adapter = this.getSyncAdapter();
  661. adapter.call('rmdir', [filename, opts]);
  662. };
  663. this.rmSync = (path, options) => {
  664. const filename = util.pathToFilename(path);
  665. const adapter = this.getSyncAdapter();
  666. adapter.call('rm', [filename, options]);
  667. };
  668. this.mkdirSync = (path, options) => {
  669. const opts = optHelpers.getMkdirOptions(options);
  670. const modeNum = util.modeToNumber(opts.mode, 0o777);
  671. const filename = util.pathToFilename(path);
  672. return this.getSyncAdapter().call('mkdir', [filename, options]);
  673. };
  674. this.mkdtempSync = (prefix, options) => {
  675. const { encoding } = optHelpers.getDefaultOpts(options);
  676. if (!prefix || typeof prefix !== 'string')
  677. throw new TypeError('filename prefix is required');
  678. util.nullCheck(prefix);
  679. const result = this.getSyncAdapter().call('mkdtemp', [prefix, options]);
  680. return (0, encoding_1.strToEncoding)(result, encoding);
  681. };
  682. this.readlinkSync = (path, options) => {
  683. const opts = optHelpers.getDefaultOpts(options);
  684. const filename = util.pathToFilename(path);
  685. const buffer = buffer_1.Buffer.from(filename);
  686. return util.bufferToEncoding(buffer, opts.encoding);
  687. };
  688. this.truncateSync = (id, len) => {
  689. if ((0, util_2.isFd)(id))
  690. return this.ftruncateSync(id, len);
  691. const filename = util.pathToFilename(id);
  692. this.getSyncAdapter().call('trunc', [filename, Number(len) || 0]);
  693. };
  694. this.ftruncateSync = (fd, len) => {
  695. const filename = this.getFileName(fd);
  696. this.truncateSync(filename, len);
  697. };
  698. this.unlinkSync = (path) => {
  699. const filename = util.pathToFilename(path);
  700. this.getSyncAdapter().call('unlink', [filename]);
  701. };
  702. this.readdirSync = (path, options) => {
  703. const opts = optHelpers.getReaddirOptions(options);
  704. const filename = util.pathToFilename(path);
  705. const adapter = this.getSyncAdapter();
  706. const list = adapter.call('readdir', [filename]);
  707. if (opts.withFileTypes) {
  708. const res = [];
  709. for (const entry of list)
  710. res.push(new FsaNodeDirent_1.FsaNodeDirent(entry.name, entry.kind));
  711. return res;
  712. }
  713. else {
  714. const res = [];
  715. for (const entry of list) {
  716. const buffer = buffer_1.Buffer.from(entry.name);
  717. res.push(util.bufferToEncoding(buffer, opts.encoding));
  718. }
  719. return res;
  720. }
  721. };
  722. this.realpathSync = (path, options) => {
  723. let filename = util.pathToFilename(path);
  724. const { encoding } = optHelpers.getRealpathOptions(options);
  725. if (filename[0] !== "/" /* FsaToNodeConstants.Separator */)
  726. filename = "/" /* FsaToNodeConstants.Separator */ + filename;
  727. return (0, encoding_1.strToEncoding)(filename, encoding);
  728. };
  729. this.readSync = (fd, buffer, offset, length, position) => {
  730. (0, util_2.validateFd)(fd);
  731. const filename = this.getFileName(fd);
  732. const adapter = this.getSyncAdapter();
  733. const uint8 = adapter.call('read', [filename, position, length]);
  734. const dest = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
  735. dest.set(uint8, offset);
  736. return uint8.length;
  737. };
  738. this.writeSync = (fd, a, b, c, d) => {
  739. const [, buf, offset, length, position] = util.getWriteSyncArgs(fd, a, b, c, d);
  740. const filename = this.getFileName(fd);
  741. const data = new Uint8Array(buf.buffer, buf.byteOffset + offset, length);
  742. return this.getSyncAdapter().call('write', [filename, data, position || null]);
  743. };
  744. this.openSync = (path, flags, mode = 438 /* MODE.DEFAULT */) => {
  745. const modeNum = util.modeToNumber(mode);
  746. const filename = util.pathToFilename(path);
  747. const flagsNum = util.flagsToNumber(flags);
  748. const adapter = this.getSyncAdapter();
  749. const handle = adapter.call('open', [filename, flagsNum, modeNum]);
  750. const openFile = this.__open2(handle, filename, flagsNum, modeNum);
  751. return openFile.fd;
  752. };
  753. this.writevSync = (fd, buffers, position) => {
  754. if (buffers.length === 0)
  755. return 0;
  756. let bytesWritten = 0;
  757. bytesWritten += this.writeSync(fd, buffers[0], 0, buffers[0].byteLength, position);
  758. for (let i = 1; i < buffers.length; i++) {
  759. bytesWritten += this.writeSync(fd, buffers[i], 0, buffers[i].byteLength, null);
  760. }
  761. return bytesWritten;
  762. };
  763. this.fdatasyncSync = noop;
  764. this.fsyncSync = noop;
  765. this.chmodSync = noop;
  766. this.chownSync = noop;
  767. this.fchmodSync = noop;
  768. this.fchownSync = noop;
  769. this.futimesSync = noop;
  770. this.lchmodSync = noop;
  771. this.lchownSync = noop;
  772. this.utimesSync = noop;
  773. this.lutimesSync = noop;
  774. this.cpSync = notImplemented;
  775. this.opendirSync = notImplemented;
  776. this.statfsSync = notImplemented;
  777. this.readvSync = notImplemented;
  778. this.globSync = notImplemented;
  779. this.symlinkSync = notSupported;
  780. this.linkSync = notSupported;
  781. // ---------------------------------------------------------- FsCommonObjects
  782. this.F_OK = constants_2.constants.F_OK;
  783. this.R_OK = constants_2.constants.R_OK;
  784. this.W_OK = constants_2.constants.W_OK;
  785. this.X_OK = constants_2.constants.X_OK;
  786. this.constants = constants_2.constants;
  787. this.Dirent = FsaNodeDirent_1.FsaNodeDirent;
  788. this.Stats = (FsaNodeStats_1.FsaNodeStats);
  789. this.WriteStream = FsaNodeWriteStream_1.FsaNodeWriteStream;
  790. this.ReadStream = FsaNodeReadStream_1.FsaNodeReadStream;
  791. this.StatFs = 0;
  792. this.Dir = 0;
  793. this.StatsWatcher = 0;
  794. this.FSWatcher = 0;
  795. }
  796. async getHandleStats(bigint, handle) {
  797. let size = 0;
  798. if (handle.kind === 'file') {
  799. const file = handle;
  800. const fileData = await file.getFile();
  801. size = fileData.size;
  802. }
  803. const stats = new FsaNodeStats_1.FsaNodeStats(bigint, bigint ? BigInt(size) : size, handle.kind);
  804. return stats;
  805. }
  806. rmAll(callback) {
  807. (async () => {
  808. const root = await this.root;
  809. for await (const name of root.keys()) {
  810. await root.removeEntry(name, { recursive: true });
  811. }
  812. })().then(() => callback(null), error => callback(error));
  813. }
  814. }
  815. exports.FsaNodeFs = FsaNodeFs;
  816. //# sourceMappingURL=FsaNodeFs.js.map