hfs.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. /**
  2. * @fileoverview The main file for the humanfs package.
  3. * @author Nicholas C. Zakas
  4. */
  5. /* global URL, TextDecoder, TextEncoder */
  6. //-----------------------------------------------------------------------------
  7. // Types
  8. //-----------------------------------------------------------------------------
  9. /** @typedef {import("@humanfs/types").HfsImpl} HfsImpl */
  10. /** @typedef {import("@humanfs/types").HfsDirectoryEntry} HfsDirectoryEntry */
  11. /** @typedef {import("@humanfs/types").HfsWalkEntry} HfsWalkEntry */
  12. //-----------------------------------------------------------------------------
  13. // Helpers
  14. //-----------------------------------------------------------------------------
  15. const decoder = new TextDecoder();
  16. const encoder = new TextEncoder();
  17. /**
  18. * Error to represent when a method is missing on an impl.
  19. */
  20. export class NoSuchMethodError extends Error {
  21. /**
  22. * Creates a new instance.
  23. * @param {string} methodName The name of the method that was missing.
  24. */
  25. constructor(methodName) {
  26. super(`Method "${methodName}" does not exist on impl.`);
  27. }
  28. }
  29. /**
  30. * Error to represent when a method is not supported on an impl. This happens
  31. * when a method on `Hfs` is called with one name and the corresponding method
  32. * on the impl has a different name. (Example: `text()` and `bytes()`.)
  33. */
  34. export class MethodNotSupportedError extends Error {
  35. /**
  36. * Creates a new instance.
  37. * @param {string} methodName The name of the method that was missing.
  38. */
  39. constructor(methodName) {
  40. super(`Method "${methodName}" is not supported on this impl.`);
  41. }
  42. }
  43. /**
  44. * Error to represent when an impl is already set.
  45. */
  46. export class ImplAlreadySetError extends Error {
  47. /**
  48. * Creates a new instance.
  49. */
  50. constructor() {
  51. super(`Implementation already set.`);
  52. }
  53. }
  54. /**
  55. * Asserts that the given path is a valid file path.
  56. * @param {any} fileOrDirPath The path to check.
  57. * @returns {void}
  58. * @throws {TypeError} When the path is not a non-empty string.
  59. */
  60. function assertValidFileOrDirPath(fileOrDirPath) {
  61. if (
  62. !fileOrDirPath ||
  63. (!(fileOrDirPath instanceof URL) && typeof fileOrDirPath !== "string")
  64. ) {
  65. throw new TypeError("Path must be a non-empty string or URL.");
  66. }
  67. }
  68. /**
  69. * Asserts that the given file contents are valid.
  70. * @param {any} contents The contents to check.
  71. * @returns {void}
  72. * @throws {TypeError} When the contents are not a string or ArrayBuffer.
  73. */
  74. function assertValidFileContents(contents) {
  75. if (
  76. typeof contents !== "string" &&
  77. !(contents instanceof ArrayBuffer) &&
  78. !ArrayBuffer.isView(contents)
  79. ) {
  80. throw new TypeError(
  81. "File contents must be a string, ArrayBuffer, or ArrayBuffer view.",
  82. );
  83. }
  84. }
  85. /**
  86. * Converts the given contents to Uint8Array.
  87. * @param {any} contents The data to convert.
  88. * @returns {Uint8Array} The converted Uint8Array.
  89. * @throws {TypeError} When the contents are not a string or ArrayBuffer.
  90. */
  91. function toUint8Array(contents) {
  92. if (contents instanceof Uint8Array) {
  93. return contents;
  94. }
  95. if (typeof contents === "string") {
  96. return encoder.encode(contents);
  97. }
  98. if (contents instanceof ArrayBuffer) {
  99. return new Uint8Array(contents);
  100. }
  101. if (ArrayBuffer.isView(contents)) {
  102. const bytes = contents.buffer.slice(
  103. contents.byteOffset,
  104. contents.byteOffset + contents.byteLength,
  105. );
  106. return new Uint8Array(bytes);
  107. }
  108. throw new TypeError(
  109. "Invalid contents type. Expected string or ArrayBuffer.",
  110. );
  111. }
  112. //-----------------------------------------------------------------------------
  113. // Exports
  114. //-----------------------------------------------------------------------------
  115. /**
  116. * A class representing a log entry.
  117. */
  118. export class LogEntry {
  119. /**
  120. * The type of log entry.
  121. * @type {string}
  122. */
  123. type;
  124. /**
  125. * The data associated with the log entry.
  126. * @type {any}
  127. */
  128. data;
  129. /**
  130. * The time at which the log entry was created.
  131. * @type {number}
  132. */
  133. timestamp = Date.now();
  134. /**
  135. * Creates a new instance.
  136. * @param {string} type The type of log entry.
  137. * @param {any} [data] The data associated with the log entry.
  138. */
  139. constructor(type, data) {
  140. this.type = type;
  141. this.data = data;
  142. }
  143. }
  144. /**
  145. * A class representing a file system utility library.
  146. * @implements {HfsImpl}
  147. */
  148. export class Hfs {
  149. /**
  150. * The base implementation for this instance.
  151. * @type {HfsImpl}
  152. */
  153. #baseImpl;
  154. /**
  155. * The current implementation for this instance.
  156. * @type {HfsImpl}
  157. */
  158. #impl;
  159. /**
  160. * A map of log names to their corresponding entries.
  161. * @type {Map<string,Array<LogEntry>>}
  162. */
  163. #logs = new Map();
  164. /**
  165. * Creates a new instance.
  166. * @param {object} options The options for the instance.
  167. * @param {HfsImpl} options.impl The implementation to use.
  168. */
  169. constructor({ impl }) {
  170. this.#baseImpl = impl;
  171. this.#impl = impl;
  172. }
  173. /**
  174. * Logs an entry onto all currently open logs.
  175. * @param {string} methodName The name of the method being called.
  176. * @param {...*} args The arguments to the method.
  177. * @returns {void}
  178. */
  179. #log(methodName, ...args) {
  180. for (const logs of this.#logs.values()) {
  181. logs.push(new LogEntry("call", { methodName, args }));
  182. }
  183. }
  184. /**
  185. * Starts a new log with the given name.
  186. * @param {string} name The name of the log to start;
  187. * @returns {void}
  188. * @throws {Error} When the log already exists.
  189. * @throws {TypeError} When the name is not a non-empty string.
  190. */
  191. logStart(name) {
  192. if (!name || typeof name !== "string") {
  193. throw new TypeError("Log name must be a non-empty string.");
  194. }
  195. if (this.#logs.has(name)) {
  196. throw new Error(`Log "${name}" already exists.`);
  197. }
  198. this.#logs.set(name, []);
  199. }
  200. /**
  201. * Ends a log with the given name and returns the entries.
  202. * @param {string} name The name of the log to end.
  203. * @returns {Array<LogEntry>} The entries in the log.
  204. * @throws {Error} When the log does not exist.
  205. */
  206. logEnd(name) {
  207. if (this.#logs.has(name)) {
  208. const logs = this.#logs.get(name);
  209. this.#logs.delete(name);
  210. return logs;
  211. }
  212. throw new Error(`Log "${name}" does not exist.`);
  213. }
  214. /**
  215. * Determines if the current implementation is the base implementation.
  216. * @returns {boolean} True if the current implementation is the base implementation.
  217. */
  218. isBaseImpl() {
  219. return this.#impl === this.#baseImpl;
  220. }
  221. /**
  222. * Sets the implementation for this instance.
  223. * @param {object} impl The implementation to use.
  224. * @returns {void}
  225. */
  226. setImpl(impl) {
  227. this.#log("implSet", impl);
  228. if (this.#impl !== this.#baseImpl) {
  229. throw new ImplAlreadySetError();
  230. }
  231. this.#impl = impl;
  232. }
  233. /**
  234. * Resets the implementation for this instance back to its original.
  235. * @returns {void}
  236. */
  237. resetImpl() {
  238. this.#log("implReset");
  239. this.#impl = this.#baseImpl;
  240. }
  241. /**
  242. * Asserts that the given method exists on the current implementation.
  243. * @param {string} methodName The name of the method to check.
  244. * @returns {void}
  245. * @throws {NoSuchMethodError} When the method does not exist on the current implementation.
  246. */
  247. #assertImplMethod(methodName) {
  248. if (typeof this.#impl[methodName] !== "function") {
  249. throw new NoSuchMethodError(methodName);
  250. }
  251. }
  252. /**
  253. * Asserts that the given method exists on the current implementation, and if not,
  254. * throws an error with a different method name.
  255. * @param {string} methodName The name of the method to check.
  256. * @param {string} targetMethodName The name of the method that should be reported
  257. * as an error when methodName does not exist.
  258. * @returns {void}
  259. * @throws {NoSuchMethodError} When the method does not exist on the current implementation.
  260. */
  261. #assertImplMethodAlt(methodName, targetMethodName) {
  262. if (typeof this.#impl[methodName] !== "function") {
  263. throw new MethodNotSupportedError(targetMethodName);
  264. }
  265. }
  266. /**
  267. * Calls the given method on the current implementation.
  268. * @param {string} methodName The name of the method to call.
  269. * @param {...any} args The arguments to the method.
  270. * @returns {any} The return value from the method.
  271. * @throws {NoSuchMethodError} When the method does not exist on the current implementation.
  272. */
  273. #callImplMethod(methodName, ...args) {
  274. this.#log(methodName, ...args);
  275. this.#assertImplMethod(methodName);
  276. return this.#impl[methodName](...args);
  277. }
  278. /**
  279. * Calls the given method on the current implementation and doesn't log the call.
  280. * @param {string} methodName The name of the method to call.
  281. * @param {...any} args The arguments to the method.
  282. * @returns {any} The return value from the method.
  283. * @throws {NoSuchMethodError} When the method does not exist on the current implementation.
  284. */
  285. #callImplMethodWithoutLog(methodName, ...args) {
  286. this.#assertImplMethod(methodName);
  287. return this.#impl[methodName](...args);
  288. }
  289. /**
  290. * Calls the given method on the current implementation but logs a different method name.
  291. * @param {string} methodName The name of the method to call.
  292. * @param {string} targetMethodName The name of the method to log.
  293. * @param {...any} args The arguments to the method.
  294. * @returns {any} The return value from the method.
  295. * @throws {NoSuchMethodError} When the method does not exist on the current implementation.
  296. */
  297. #callImplMethodAlt(methodName, targetMethodName, ...args) {
  298. this.#log(targetMethodName, ...args);
  299. this.#assertImplMethodAlt(methodName, targetMethodName);
  300. return this.#impl[methodName](...args);
  301. }
  302. /**
  303. * Reads the given file and returns the contents as text. Assumes UTF-8 encoding.
  304. * @param {string|URL} filePath The file to read.
  305. * @returns {Promise<string|undefined>} The contents of the file.
  306. * @throws {NoSuchMethodError} When the method does not exist on the current implementation.
  307. * @throws {TypeError} When the file path is not a non-empty string.
  308. */
  309. async text(filePath) {
  310. assertValidFileOrDirPath(filePath);
  311. const result = await this.#callImplMethodAlt("bytes", "text", filePath);
  312. return result ? decoder.decode(result) : undefined;
  313. }
  314. /**
  315. * Reads the given file and returns the contents as JSON. Assumes UTF-8 encoding.
  316. * @param {string|URL} filePath The file to read.
  317. * @returns {Promise<any|undefined>} The contents of the file as JSON.
  318. * @throws {NoSuchMethodError} When the method does not exist on the current implementation.
  319. * @throws {SyntaxError} When the file contents are not valid JSON.
  320. * @throws {TypeError} When the file path is not a non-empty string.
  321. */
  322. async json(filePath) {
  323. assertValidFileOrDirPath(filePath);
  324. const result = await this.#callImplMethodAlt("bytes", "json", filePath);
  325. return result ? JSON.parse(decoder.decode(result)) : undefined;
  326. }
  327. /**
  328. * Reads the given file and returns the contents as an ArrayBuffer.
  329. * @param {string|URL} filePath The file to read.
  330. * @returns {Promise<ArrayBuffer|undefined>} The contents of the file as an ArrayBuffer.
  331. * @throws {NoSuchMethodError} When the method does not exist on the current implementation.
  332. * @throws {TypeError} When the file path is not a non-empty string.
  333. * @deprecated Use bytes() instead.
  334. */
  335. async arrayBuffer(filePath) {
  336. assertValidFileOrDirPath(filePath);
  337. const result = await this.#callImplMethodAlt(
  338. "bytes",
  339. "arrayBuffer",
  340. filePath,
  341. );
  342. return result?.buffer;
  343. }
  344. /**
  345. * Reads the given file and returns the contents as an Uint8Array.
  346. * @param {string|URL} filePath The file to read.
  347. * @returns {Promise<Uint8Array|undefined>} The contents of the file as an Uint8Array.
  348. * @throws {NoSuchMethodError} When the method does not exist on the current implementation.
  349. * @throws {TypeError} When the file path is not a non-empty string.
  350. */
  351. async bytes(filePath) {
  352. assertValidFileOrDirPath(filePath);
  353. return this.#callImplMethod("bytes", filePath);
  354. }
  355. /**
  356. * Writes the given data to the given file. Creates any necessary directories along the way.
  357. * If the data is a string, UTF-8 encoding is used.
  358. * @param {string|URL} filePath The file to write.
  359. * @param {string|ArrayBuffer|ArrayBufferView} contents The data to write.
  360. * @returns {Promise<void>} A promise that resolves when the file is written.
  361. * @throws {NoSuchMethodError} When the method does not exist on the current implementation.
  362. * @throws {TypeError} When the file path is not a non-empty string.
  363. */
  364. async write(filePath, contents) {
  365. assertValidFileOrDirPath(filePath);
  366. assertValidFileContents(contents);
  367. this.#log("write", filePath, contents);
  368. let value = toUint8Array(contents);
  369. return this.#callImplMethodWithoutLog("write", filePath, value);
  370. }
  371. /**
  372. * Appends the given data to the given file. Creates any necessary directories along the way.
  373. * If the data is a string, UTF-8 encoding is used.
  374. * @param {string|URL} filePath The file to append to.
  375. * @param {string|ArrayBuffer|ArrayBufferView} contents The data to append.
  376. * @returns {Promise<void>} A promise that resolves when the file is appended to.
  377. * @throws {NoSuchMethodError} When the method does not exist on the current implementation.
  378. * @throws {TypeError} When the file path is not a non-empty string.
  379. * @throws {TypeError} When the file contents are not a string or ArrayBuffer.
  380. * @throws {Error} When the file cannot be appended to.
  381. */
  382. async append(filePath, contents) {
  383. assertValidFileOrDirPath(filePath);
  384. assertValidFileContents(contents);
  385. this.#log("append", filePath, contents);
  386. let value = toUint8Array(contents);
  387. return this.#callImplMethodWithoutLog("append", filePath, value);
  388. }
  389. /**
  390. * Determines if the given file exists.
  391. * @param {string|URL} filePath The file to check.
  392. * @returns {Promise<boolean>} True if the file exists.
  393. * @throws {NoSuchMethodError} When the method does not exist on the current implementation.
  394. * @throws {TypeError} When the file path is not a non-empty string.
  395. */
  396. async isFile(filePath) {
  397. assertValidFileOrDirPath(filePath);
  398. return this.#callImplMethod("isFile", filePath);
  399. }
  400. /**
  401. * Determines if the given directory exists.
  402. * @param {string|URL} dirPath The directory to check.
  403. * @returns {Promise<boolean>} True if the directory exists.
  404. * @throws {NoSuchMethodError} When the method does not exist on the current implementation.
  405. * @throws {TypeError} When the directory path is not a non-empty string.
  406. */
  407. async isDirectory(dirPath) {
  408. assertValidFileOrDirPath(dirPath);
  409. return this.#callImplMethod("isDirectory", dirPath);
  410. }
  411. /**
  412. * Creates the given directory.
  413. * @param {string|URL} dirPath The directory to create.
  414. * @returns {Promise<void>} A promise that resolves when the directory is created.
  415. * @throws {NoSuchMethodError} When the method does not exist on the current implementation.
  416. * @throws {TypeError} When the directory path is not a non-empty string.
  417. */
  418. async createDirectory(dirPath) {
  419. assertValidFileOrDirPath(dirPath);
  420. return this.#callImplMethod("createDirectory", dirPath);
  421. }
  422. /**
  423. * Deletes the given file or empty directory.
  424. * @param {string|URL} filePath The file to delete.
  425. * @returns {Promise<boolean>} A promise that resolves when the file or
  426. * directory is deleted, true if the file or directory is deleted, false
  427. * if the file or directory does not exist.
  428. * @throws {NoSuchMethodError} When the method does not exist on the current implementation.
  429. * @throws {TypeError} When the file path is not a non-empty string.
  430. */
  431. async delete(filePath) {
  432. assertValidFileOrDirPath(filePath);
  433. return this.#callImplMethod("delete", filePath);
  434. }
  435. /**
  436. * Deletes the given file or directory recursively.
  437. * @param {string|URL} dirPath The directory to delete.
  438. * @returns {Promise<boolean>} A promise that resolves when the file or
  439. * directory is deleted, true if the file or directory is deleted, false
  440. * if the file or directory does not exist.
  441. * @throws {NoSuchMethodError} When the method does not exist on the current implementation.
  442. * @throws {TypeError} When the directory path is not a non-empty string.
  443. */
  444. async deleteAll(dirPath) {
  445. assertValidFileOrDirPath(dirPath);
  446. return this.#callImplMethod("deleteAll", dirPath);
  447. }
  448. /**
  449. * Returns a list of directory entries for the given path.
  450. * @param {string|URL} dirPath The path to the directory to read.
  451. * @returns {AsyncIterable<HfsDirectoryEntry>} A promise that resolves with the
  452. * directory entries.
  453. * @throws {TypeError} If the directory path is not a string or URL.
  454. * @throws {Error} If the directory cannot be read.
  455. */
  456. async *list(dirPath) {
  457. assertValidFileOrDirPath(dirPath);
  458. yield* await this.#callImplMethod("list", dirPath);
  459. }
  460. /**
  461. * Walks a directory using a depth-first traversal and returns the entries
  462. * from the traversal.
  463. * @param {string|URL} dirPath The path to the directory to walk.
  464. * @param {Object} [options] The options for the walk.
  465. * @param {(entry:HfsWalkEntry) => Promise<boolean>|boolean} [options.directoryFilter] A filter function to determine
  466. * if a directory's entries should be included in the walk.
  467. * @param {(entry:HfsWalkEntry) => Promise<boolean>|boolean} [options.entryFilter] A filter function to determine if
  468. * an entry should be included in the walk.
  469. * @returns {AsyncIterable<HfsWalkEntry>} A promise that resolves with the
  470. * directory entries.
  471. * @throws {TypeError} If the directory path is not a string or URL.
  472. * @throws {Error} If the directory cannot be read.
  473. */
  474. async *walk(
  475. dirPath,
  476. { directoryFilter = () => true, entryFilter = () => true } = {},
  477. ) {
  478. assertValidFileOrDirPath(dirPath);
  479. this.#log("walk", dirPath, { directoryFilter, entryFilter });
  480. // inner function for recursion without additional logging
  481. const walk = async function* (
  482. dirPath,
  483. { directoryFilter, entryFilter, parentPath = "", depth = 1 },
  484. ) {
  485. let dirEntries;
  486. try {
  487. dirEntries = await this.#callImplMethodWithoutLog(
  488. "list",
  489. dirPath,
  490. );
  491. } catch (error) {
  492. // if the directory does not exist then return an empty array
  493. if (error.code === "ENOENT") {
  494. return;
  495. }
  496. // otherwise, rethrow the error
  497. throw error;
  498. }
  499. for await (const listEntry of dirEntries) {
  500. const walkEntry = {
  501. path: listEntry.name,
  502. depth,
  503. ...listEntry,
  504. };
  505. if (parentPath) {
  506. walkEntry.path = `${parentPath}/${walkEntry.path}`;
  507. }
  508. // first emit the entry but only if the entry filter returns true
  509. let shouldEmitEntry = entryFilter(walkEntry);
  510. if (shouldEmitEntry.then) {
  511. shouldEmitEntry = await shouldEmitEntry;
  512. }
  513. if (shouldEmitEntry) {
  514. yield walkEntry;
  515. }
  516. // if it's a directory then yield the entry and walk the directory
  517. if (listEntry.isDirectory) {
  518. // if the directory filter returns false, skip the directory
  519. let shouldWalkDirectory = directoryFilter(walkEntry);
  520. if (shouldWalkDirectory.then) {
  521. shouldWalkDirectory = await shouldWalkDirectory;
  522. }
  523. if (!shouldWalkDirectory) {
  524. continue;
  525. }
  526. // make sure there's a trailing slash on the directory path before appending
  527. const directoryPath =
  528. dirPath instanceof URL
  529. ? new URL(
  530. listEntry.name,
  531. dirPath.href.endsWith("/")
  532. ? dirPath.href
  533. : `${dirPath.href}/`,
  534. )
  535. : `${dirPath.endsWith("/") ? dirPath : `${dirPath}/`}${listEntry.name}`;
  536. yield* walk(directoryPath, {
  537. directoryFilter,
  538. entryFilter,
  539. parentPath: walkEntry.path,
  540. depth: depth + 1,
  541. });
  542. }
  543. }
  544. }.bind(this);
  545. yield* walk(dirPath, { directoryFilter, entryFilter });
  546. }
  547. /**
  548. * Returns the size of the given file.
  549. * @param {string|URL} filePath The path to the file to read.
  550. * @returns {Promise<number>} A promise that resolves with the size of the file.
  551. * @throws {TypeError} If the file path is not a string or URL.
  552. * @throws {Error} If the file cannot be read.
  553. */
  554. async size(filePath) {
  555. assertValidFileOrDirPath(filePath);
  556. return this.#callImplMethod("size", filePath);
  557. }
  558. /**
  559. * Returns the last modified timestamp of the given file or directory.
  560. * @param {string|URL} fileOrDirPath The path to the file or directory.
  561. * @returns {Promise<Date|undefined>} A promise that resolves with the last modified date
  562. * or undefined if the file or directory does not exist.
  563. * @throws {TypeError} If the path is not a string or URL.
  564. */
  565. async lastModified(fileOrDirPath) {
  566. assertValidFileOrDirPath(fileOrDirPath);
  567. return this.#callImplMethod("lastModified", fileOrDirPath);
  568. }
  569. /**
  570. * Copys a file from one location to another.
  571. * @param {string|URL} source The path to the file to copy.
  572. * @param {string|URL} destination The path to the new file.
  573. * @returns {Promise<void>} A promise that resolves when the file is copied.
  574. * @throws {TypeError} If the file path is not a string or URL.
  575. * @throws {Error} If the file cannot be copied.
  576. */
  577. async copy(source, destination) {
  578. assertValidFileOrDirPath(source);
  579. assertValidFileOrDirPath(destination);
  580. return this.#callImplMethod("copy", source, destination);
  581. }
  582. /**
  583. * Copies a file or directory from one location to another.
  584. * @param {string|URL} source The path to the file or directory to copy.
  585. * @param {string|URL} destination The path to copy the file or directory to.
  586. * @returns {Promise<void>} A promise that resolves when the file or directory is
  587. * copied.
  588. * @throws {TypeError} If the directory path is not a string or URL.
  589. * @throws {Error} If the directory cannot be copied.
  590. */
  591. async copyAll(source, destination) {
  592. assertValidFileOrDirPath(source);
  593. assertValidFileOrDirPath(destination);
  594. return this.#callImplMethod("copyAll", source, destination);
  595. }
  596. /**
  597. * Moves a file from the source path to the destination path.
  598. * @param {string|URL} source The location of the file to move.
  599. * @param {string|URL} destination The destination of the file to move.
  600. * @returns {Promise<void>} A promise that resolves when the move is complete.
  601. * @throws {TypeError} If the file or directory paths are not strings.
  602. * @throws {Error} If the file or directory cannot be moved.
  603. */
  604. async move(source, destination) {
  605. assertValidFileOrDirPath(source);
  606. assertValidFileOrDirPath(destination);
  607. return this.#callImplMethod("move", source, destination);
  608. }
  609. /**
  610. * Moves a file or directory from one location to another.
  611. * @param {string|URL} source The path to the file or directory to move.
  612. * @param {string|URL} destination The path to move the file or directory to.
  613. * @returns {Promise<void>} A promise that resolves when the file or directory is
  614. * moved.
  615. * @throws {TypeError} If the source is not a string or URL.
  616. * @throws {TypeError} If the destination is not a string or URL.
  617. * @throws {Error} If the file or directory cannot be moved.
  618. */
  619. async moveAll(source, destination) {
  620. assertValidFileOrDirPath(source);
  621. assertValidFileOrDirPath(destination);
  622. return this.#callImplMethod("moveAll", source, destination);
  623. }
  624. }