@themartiancompany/opfs 1.8.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/COPYING +674 -0
  2. package/README.cn.md +301 -0
  3. package/README.md +241 -0
  4. package/dist/main.cjs +1840 -0
  5. package/dist/main.cjs.map +1 -0
  6. package/dist/main.mjs +1751 -0
  7. package/dist/main.mjs.map +1 -0
  8. package/dist/types.d.ts +920 -0
  9. package/dist/types.d.ts.map +1 -0
  10. package/docs/README.md +115 -0
  11. package/docs/classes/SyncMessenger.md +42 -0
  12. package/docs/functions/appendFile.md +29 -0
  13. package/docs/functions/appendFileSync.md +26 -0
  14. package/docs/functions/assertAbsolutePath.md +29 -0
  15. package/docs/functions/assertFileUrl.md +29 -0
  16. package/docs/functions/connectSyncAgent.md +25 -0
  17. package/docs/functions/copy.md +35 -0
  18. package/docs/functions/copySync.md +30 -0
  19. package/docs/functions/createFile.md +27 -0
  20. package/docs/functions/createFileSync.md +25 -0
  21. package/docs/functions/deleteTemp.md +23 -0
  22. package/docs/functions/deleteTempSync.md +19 -0
  23. package/docs/functions/downloadFile.md +58 -0
  24. package/docs/functions/emptyDir.md +28 -0
  25. package/docs/functions/emptyDirSync.md +25 -0
  26. package/docs/functions/exists.md +29 -0
  27. package/docs/functions/existsSync.md +26 -0
  28. package/docs/functions/generateTempPath.md +27 -0
  29. package/docs/functions/getFileDataByHandle.md +27 -0
  30. package/docs/functions/getSyncMessenger.md +22 -0
  31. package/docs/functions/isDirectoryHandle.md +27 -0
  32. package/docs/functions/isFileHandle.md +27 -0
  33. package/docs/functions/isFileHandleLike.md +27 -0
  34. package/docs/functions/isOPFSSupported.md +21 -0
  35. package/docs/functions/isTempPath.md +27 -0
  36. package/docs/functions/mkTemp.md +28 -0
  37. package/docs/functions/mkTempSync.md +25 -0
  38. package/docs/functions/mkdir.md +27 -0
  39. package/docs/functions/mkdirSync.md +25 -0
  40. package/docs/functions/move.md +34 -0
  41. package/docs/functions/moveSync.md +30 -0
  42. package/docs/functions/pruneTemp.md +28 -0
  43. package/docs/functions/pruneTempSync.md +25 -0
  44. package/docs/functions/readBlobFile.md +28 -0
  45. package/docs/functions/readBlobFileSync.md +25 -0
  46. package/docs/functions/readDir.md +28 -0
  47. package/docs/functions/readDirSync.md +26 -0
  48. package/docs/functions/readFile.md +132 -0
  49. package/docs/functions/readFileSync.md +70 -0
  50. package/docs/functions/readJsonFile.md +35 -0
  51. package/docs/functions/readJsonFileSync.md +31 -0
  52. package/docs/functions/readTextFile.md +28 -0
  53. package/docs/functions/readTextFileSync.md +25 -0
  54. package/docs/functions/remove.md +27 -0
  55. package/docs/functions/removeSync.md +25 -0
  56. package/docs/functions/setSyncMessenger.md +26 -0
  57. package/docs/functions/startSyncAgent.md +21 -0
  58. package/docs/functions/stat.md +27 -0
  59. package/docs/functions/statSync.md +25 -0
  60. package/docs/functions/toFileSystemHandleLike.md +27 -0
  61. package/docs/functions/unzip.md +32 -0
  62. package/docs/functions/unzipFromUrl.md +36 -0
  63. package/docs/functions/unzipSync.md +26 -0
  64. package/docs/functions/uploadFile.md +33 -0
  65. package/docs/functions/writeFile.md +32 -0
  66. package/docs/functions/writeFileSync.md +30 -0
  67. package/docs/functions/zip.md +65 -0
  68. package/docs/functions/zipFromUrl.md +63 -0
  69. package/docs/functions/zipSync.md +55 -0
  70. package/docs/interfaces/CopyOptions.md +17 -0
  71. package/docs/interfaces/DownloadFileTempResponse.md +18 -0
  72. package/docs/interfaces/ErrorLike.md +18 -0
  73. package/docs/interfaces/ExistsOptions.md +18 -0
  74. package/docs/interfaces/FileLike.md +21 -0
  75. package/docs/interfaces/FileSystemFileHandleLike.md +25 -0
  76. package/docs/interfaces/FileSystemHandleLike.md +22 -0
  77. package/docs/interfaces/MoveOptions.md +17 -0
  78. package/docs/interfaces/ReadDirEntry.md +18 -0
  79. package/docs/interfaces/ReadDirEntrySync.md +18 -0
  80. package/docs/interfaces/ReadDirOptions.md +17 -0
  81. package/docs/interfaces/ReadOptions.md +17 -0
  82. package/docs/interfaces/SyncAgentOptions.md +19 -0
  83. package/docs/interfaces/TempOptions.md +19 -0
  84. package/docs/interfaces/UploadRequestInit.md +21 -0
  85. package/docs/interfaces/WriteOptions.md +19 -0
  86. package/docs/interfaces/ZipOptions.md +17 -0
  87. package/docs/type-aliases/FileEncoding.md +15 -0
  88. package/docs/type-aliases/FsRequestInit.md +15 -0
  89. package/docs/type-aliases/ReadFileContent.md +15 -0
  90. package/docs/type-aliases/WriteFileContent.md +15 -0
  91. package/docs/type-aliases/WriteSyncFileContent.md +16 -0
  92. package/docs/variables/CURRENT_DIR.md +15 -0
  93. package/docs/variables/NOT_FOUND_ERROR.md +18 -0
  94. package/docs/variables/ROOT_DIR.md +15 -0
  95. package/docs/variables/TMP_DIR.md +15 -0
  96. package/package.json +141 -0
  97. package/src/fs/assertions.ts +63 -0
  98. package/src/fs/constants.ts +63 -0
  99. package/src/fs/defines.ts +352 -0
  100. package/src/fs/helpers.ts +338 -0
  101. package/src/fs/opfs_core.ts +413 -0
  102. package/src/fs/opfs_download.ts +174 -0
  103. package/src/fs/opfs_ext.ts +504 -0
  104. package/src/fs/opfs_tmp.ts +131 -0
  105. package/src/fs/opfs_unzip.ts +168 -0
  106. package/src/fs/opfs_upload.ts +126 -0
  107. package/src/fs/opfs_zip.ts +314 -0
  108. package/src/fs/support.ts +36 -0
  109. package/src/fs/utils.ts +176 -0
  110. package/src/mod.ts +41 -0
  111. package/src/worker/helpers.ts +168 -0
  112. package/src/worker/opfs_worker.ts +298 -0
  113. package/src/worker/opfs_worker_adapter.ts +666 -0
  114. package/src/worker/shared.ts +400 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sources":["../src/fs/assertions.ts","../src/fs/constants.ts","../src/fs/defines.ts","../src/fs/opfs_core.ts","../src/fs/opfs_download.ts","../src/fs/opfs_ext.ts","../src/fs/opfs_tmp.ts","../src/fs/opfs_unzip.ts","../src/fs/opfs_upload.ts","../src/fs/opfs_zip.ts","../src/fs/support.ts","../src/fs/utils.ts","../src/worker/opfs_worker.ts","../src/worker/shared.ts","../src/worker/opfs_worker_adapter.ts"],"sourcesContent":["// SPDX-License-Identifier: GPL-3.0-or-later\n\n/** ----------------------------------------------------------------------\n * Copyright ©\n * Jiang Jie\n * 2024, 2025\n * Pellegrino Prevete\n * 2025\n * \n * All rights reserved\n * ----------------------------------------------------------------------\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n * \n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport invariant from 'tiny-invariant';\nimport { ROOT_DIR } from './constants.ts';\n\n/**\n * Asserts that the provided path is an absolute path.\n *\n * @param path - The file path to validate.\n * @throws Will throw an error if the path is not an absolute path.\n */\nexport function\n assertAbsolutePath(\n path:\n string): void {\n invariant(\n typeof path === 'string',\n () => `Path must be a string but received ${ path }`);\n invariant(\n path[\n 0] === ROOT_DIR,\n () => `Path must start with / but received ${ path }`);\n}\n\n/**\n * Asserts that the provided URL is a valid file URL.\n *\n * @param fileUrl - The file URL to validate.\n * @throws Will throw an error if the URL is not a valid file URL.\n */\nexport function\n assertFileUrl(\n fileUrl:\n string): void {\n invariant(\n typeof fileUrl === 'string',\n () => `File url must be a string ` +\n `but received ${ fileUrl }`);\n}\n","// SPDX-License-Identifier: GPL-3.0-or-later\n\n/** ----------------------------------------------------------------------\n * Copyright ©\n * Jiang Jie\n * 2024, 2025\n * Pellegrino Prevete\n * 2025\n * \n * All rights reserved\n * ----------------------------------------------------------------------\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n * \n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nexport { ABORT_ERROR,\n TIMEOUT_ERROR } from '@happy-ts/fetch-t';\n\n/**\n * A constant representing the error thrown when a\n * file or directory is not found.\n *\n * Name of DOMException.NOT_FOUND_ERR.\n */\nexport\n const\n NOT_FOUND_ERROR =\n 'NotFoundError' as const;\n\n/**\n * A constant representing the root directory path.\n */\nexport\n const\n ROOT_DIR =\n '/' as const;\n\n/**\n * A constant representing the current directory path.\n */\nexport\n const\n CURRENT_DIR =\n '.' as const;\n\n/**\n * A constant representing the temporary directory path.\n */\nexport\n const\n TMP_DIR =\n '/tmp' as const;\n","// SPDX-License-Identifier: GPL-3.0-or-later\n\n/** ----------------------------------------------------------------------\n * Copyright ©\n * Jiang Jie\n * 2024, 2025\n * Pellegrino Prevete\n * 2025\n * \n * All rights reserved\n * ----------------------------------------------------------------------\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n * \n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { FetchInit } from '@happy-ts/fetch-t';\n\n/**\n * Represents the possible content types that can be written to a file.\n */\nexport type WriteFileContent =\n BufferSource |\n Blob |\n string;\n\n/**\n * Represents the possible content types that can be\n * written synchronously to a file.\n */\nexport type WriteSyncFileContent =\n BufferSource |\n string;\n\n/**\n * Represents the possible content types that can be read from a file.\n */\nexport type ReadFileContent =\n ArrayBuffer |\n File |\n string;\n\n/**\n * Options for reading files with specified encoding.\n */\nexport interface ReadOptions {\n /**\n * The encoding to use for reading the file's content.\n * @defaultValue `'binary'`\n */\n encoding?:\n FileEncoding;\n}\n\n/**\n * Options for writing files, including flags for creation\n * and appending.\n */\nexport interface WriteOptions {\n /**\n * Whether to create the file if it does not exist.\n * @defaultValue `true`\n */\n create?:\n boolean;\n /**\n * Whether to append to the file if it already exists.\n * @defaultValue `false`\n */\n append?:\n boolean;\n}\n\n/**\n * Options to determine the existence of a file or directory.\n */\nexport interface ExistsOptions {\n /**\n * Whether to check for the existence of a directory.\n * @defaultValue `false`\n */\n isDirectory?:\n boolean;\n /**\n * Whether to check for the existence of a file.\n * @defaultValue `false`\n */\n isFile?:\n boolean;\n}\n\n/**\n * Supported file encodings for reading and writing files.\n */\nexport type FileEncoding =\n 'binary' |\n 'utf8' |\n 'blob';\n\n/**\n * fetch-t options for download and upload.\n */\nexport type FsRequestInit =\n Omit<FetchInit,\n 'abortable' |\n 'responseType'>\n\n/**\n * fetch-t request options for uploading files.\n */\nexport interface UploadRequestInit extends FsRequestInit {\n /**\n * The filename to use when uploading the file.\n */\n filename?:\n string;\n}\n\n/**\n * Options for reading directories.\n */\nexport interface ReadDirOptions {\n /**\n * Whether to recursively read the contents of directories.\n */\n recursive:\n boolean;\n}\n\n/**\n * An entry returned by `readDir`.\n */\nexport interface ReadDirEntry {\n /**\n * The relative path of the entry from readDir the path parameter.\n */\n path:\n string;\n /**\n * The handle of the entry.\n */\n handle:\n FileSystemHandle;\n}\n\n/**\n * An entry returned by `readDirSync`.\n */\nexport interface ReadDirEntrySync {\n /**\n * The relative path of the entry from readDir the path parameter.\n */\n path:\n string;\n /**\n * The handle of the entry.\n */\n handle:\n FileSystemHandleLike;\n}\n\n/**\n * A handle to a file or directory returned by `statSync`.\n */\nexport interface FileSystemHandleLike {\n /**\n * The name of the entry.\n */\n name:\n string;\n /**\n * The kind of the entry.\n */\n kind:\n FileSystemHandleKind;\n}\n\nexport interface FileSystemFileHandleLike extends FileSystemHandleLike {\n /**\n * The type of the file.\n */\n type:\n string;\n /**\n * The size of the file.\n */\n size:\n number;\n /**\n * The last modified time of the file.\n */\n lastModified:\n number;\n}\n\n/**\n * Serializable version of Error.\n */\nexport interface ErrorLike {\n /**\n * The name of the error.\n */\n name:\n string;\n /**\n * The message of the error.\n */\n message:\n string;\n}\n\n/**\n * Serializable version of File.\n */\nexport interface FileLike {\n /**\n * The name of the file.\n */\n name:\n string;\n /**\n * The type of the file.\n */\n type:\n string;\n /**\n * The last modified time of the file.\n */\n lastModified:\n number;\n /**\n * The size of the file.\n */\n size:\n number;\n /**\n * The binary data of the file.\n */\n data:\n ArrayBuffer;\n}\n\n/**\n * Setup options of `connectSyncAgent`.\n */\nexport interface SyncAgentOptions {\n /**\n * The worker to communicate with.\n */\n worker:\n Worker |\n URL |\n string;\n /**\n * The length of the buffer to use for communication.\n */\n bufferLength?:\n number;\n /**\n * The timeout for operations.\n */\n opTimeout?:\n number;\n}\n\n/**\n * Options for `zip`.\n */\nexport interface ZipOptions {\n /**\n * Whether to preserve the root directory in the zip file.\n * @defaultValue `true`\n */\n preserveRoot:\n boolean;\n}\n\n/**\n * Options for `mkTemp`.\n */\nexport interface TempOptions {\n /**\n * Whether to create a directory.\n * eg: `mktemp -d`\n * @defaultValue `false`\n */\n isDirectory?:\n boolean;\n /**\n * The basename of the file or directory.\n * eg: `mktemp -t basename.XXX`\n * @defaultValue `tmp`\n */\n basename?:\n string;\n /**\n * The extension of the file.\n * eg: `mktemp --suffix .txt`\n */\n extname?:\n string;\n}\n\n/**\n * Options for `copy`.\n */\nexport interface CopyOptions {\n /**\n * Whether to overwrite the destination file if it already exists.\n * @defaultValue `true`\n */\n overwrite?:\n boolean;\n}\n\n/**\n * Result of `downloadFile` when the file is saved to a temporary path.\n */\nexport interface DownloadFileTempResponse {\n /**\n * The temporary path of the downloaded file to be saved.\n */\n tempFilePath:\n string;\n /**\n * The raw response.\n */\n rawResponse:\n Response;\n}\n\n/**\n * Options for `move`.\n */\nexport interface MoveOptions {\n /**\n * Whether to overwrite the destination file if it already exists.\n * @defaultValue `true`\n */\n overwrite?:\n boolean;\n}\n","// SPDX-License-Identifier: GPL-3.0-or-later\n\n/** ----------------------------------------------------------------------\n * Copyright ©\n * Jiang Jie\n * 2024, 2025\n * Pellegrino Prevete\n * 2025\n * \n * All rights reserved\n * ----------------------------------------------------------------------\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n * \n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { basename,\n dirname,\n join } from '@std/path/posix';\nimport { Err,\n Ok,\n RESULT_VOID,\n type AsyncIOResult,\n type AsyncVoidIOResult } from 'happy-rusty';\nimport { assertAbsolutePath } from './assertions.ts';\nimport { NOT_FOUND_ERROR } from './constants.ts';\nimport type { ReadDirEntry,\n ReadDirOptions,\n ReadFileContent,\n ReadOptions,\n WriteFileContent,\n WriteOptions } from './defines.ts';\nimport { getDirHandle,\n getFileHandle,\n isNotFoundError,\n isRootPath } from './helpers.ts';\nimport { isDirectoryHandle } from './utils.ts';\n\n/**\n * Creates a new file at the specified path same as `touch`.\n *\n * @param filePath - The path of the file to create.\n * @returns A promise that resolves to an `AsyncIOResult` indicating whether the file was successfully created.\n */\nexport async function\n createFile(\n filePath:\n string):\n AsyncVoidIOResult {\n assertAbsolutePath(\n filePath);\n const\n fileHandleRes =\n await getFileHandle(\n filePath,\n { create:\n true });\n return fileHandleRes.and(\n RESULT_VOID);\n}\n\n/**\n * Creates a new directory at the specified path same as `mkdir -p`.\n *\n * @param dirPath - The path where the new directory will be created.\n * @returns A promise that resolves to an `AsyncIOResult` indicating whether the directory was successfully created.\n */\nexport async function\n mkdir(\n dirPath:\n string):\n AsyncVoidIOResult {\n assertAbsolutePath(\n dirPath);\n const\n dirHandleRes =\n await getDirHandle(\n dirPath,\n { create:\n true,\n });\n return dirHandleRes.and(\n RESULT_VOID);\n}\n\n/**\n * Reads the contents of a directory at the specified path.\n *\n * @param dirPath - The path of the directory to read.\n * @param options - Options of readdir.\n * @returns A promise that resolves to an `AsyncIOResult` containing an async iterable iterator over the entries of the directory.\n */\nexport async function\n readDir(\n dirPath:\n string,\n options?:\n ReadDirOptions):\n AsyncIOResult<AsyncIterableIterator<ReadDirEntry>> {\n assertAbsolutePath(\n dirPath);\n const\n dirHandleRes =\n await getDirHandle(\n dirPath);\n async function*\n read(\n dirHandle:\n FileSystemDirectoryHandle,\n subDirPath:\n string):\n AsyncIterableIterator<ReadDirEntry> {\n const\n\t entries =\n dirHandle.entries();\n for await ( const [ name,\n handle ] of entries) {\n // relative path from `dirPath`\n const\n path =\n subDirPath === dirPath ? name : join(\n subDirPath,\n name);\n yield {\n path,\n handle,\n };\n if ( isDirectoryHandle(\n handle) && options?.recursive ) {\n yield* read(\n await dirHandle.getDirectoryHandle(\n name),\n path);\n }\n }\n }\n return dirHandleRes.andThen(\n x => Ok(\n read(\n x,\n dirPath)));\n}\n\n/**\n * Reads the content of a file at the specified path as a File.\n *\n * @param filePath - The path of the file to read.\n * @param options - Read options specifying the 'blob' encoding.\n * @returns A promise that resolves to an `AsyncIOResult` containing the file content as a File.\n */\nexport function\n readFile(\n filePath:\n string,\n options:\n ReadOptions & { encoding:\n 'blob'; }):\n AsyncIOResult<File>;\n\n/**\n * Reads the content of a file at the specified path as a string.\n *\n * @param filePath - The path of the file to read.\n * @param options - Read options specifying the 'utf8' encoding.\n * @returns A promise that resolves to an `AsyncIOResult` containing the file content as a string.\n */\nexport function\n readFile(\n filePath:\n string,\n options:\n ReadOptions & { encoding:\n 'utf8'; }):\n AsyncIOResult<string>;\n\n/**\n * Reads the content of a file at the specified path as an ArrayBuffer by default.\n *\n * @param filePath - The path of the file to read.\n * @param options - Read options specifying the 'binary' encoding.\n * @returns A promise that resolves to an `AsyncIOResult` containing the file content as an ArrayBuffer.\n */\nexport function\n readFile(\n filePath:\n string,\n options?:\n ReadOptions & { encoding:\n 'binary'; }):\n AsyncIOResult<ArrayBuffer>;\n\n/**\n * Reads the content of a file at the specified path with the specified options.\n *\n * @template T The type of the content to read from the file.\n * @param filePath - The path of the file to read.\n * @param options - Optional read options.\n * @returns A promise that resolves to an `AsyncIOResult` containing the file content.\n */\nexport async function\n readFile<T extends ReadFileContent>(\n filePath:\n string,\n options?:\n ReadOptions):\n AsyncIOResult<T> {\n assertAbsolutePath(\n filePath);\n const\n fileHandleRes =\n await getFileHandle(\n filePath);\n return fileHandleRes.andThenAsync(\n async fileHandle => {\n const\n file =\n await fileHandle.getFile();\n switch ( options?.encoding ) {\n case 'blob': {\n return Ok(file as unknown as T);\n }\n case 'utf8': {\n const\n text =\n await file.text();\n return Ok(text as unknown as T);\n }\n default: {\n const\n data =\n await file.arrayBuffer();\n return Ok(data as unknown as T);\n }\n }\n });\n}\n\n/**\n * Removes a file or directory at the specified path same as `rm -rf`.\n *\n * @param path - The path of the file or directory to remove.\n * @returns A promise that resolves to an `AsyncIOResult` indicating whether the file or directory was successfully removed.\n */\nexport async function\n remove(\n path:\n string):\n AsyncVoidIOResult {\n assertAbsolutePath(\n path);\n const\n dirPath =\n dirname(\n path);\n const\n childName =\n basename(\n path);\n const\n dirHandleRes =\n await getDirHandle(\n dirPath);\n return ( await dirHandleRes.andThenAsync(\n async (\n dirHandle):\n AsyncVoidIOResult => {\n try {\n // root\n if ( isRootPath(\n dirPath) &&\n isRootPath(\n childName) ) {\n // TODO ts not support yet\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await ( dirHandle as any ).remove(\n { recursive:\n true });\n } else {\n await dirHandle.removeEntry(\n childName,\n { recursive:\n true });\n }\n } catch (\n _error) {\n return Err(_error as DOMException);\n }\n return RESULT_VOID;\n })).orElse<Error>(\n _error => {\n // not found as success\n return isNotFoundError(\n _error) ? RESULT_VOID : Err(\n _error);\n });\n}\n\n/**\n * Retrieves the status of a file or directory at the specified path.\n *\n * @param path - The path of the file or directory to retrieve status for.\n * @returns A promise that resolves to an `AsyncIOResult` containing the `FileSystemHandle`.\n */\nexport async function\n stat(\n path:\n string):\n AsyncIOResult<FileSystemHandle> {\n assertAbsolutePath(\n path);\n const\n dirPath =\n dirname(\n path);\n const\n childName =\n basename(\n path);\n const\n dirHandleRes =\n await getDirHandle(\n dirPath);\n if ( !childName ) {\n // root\n return dirHandleRes;\n }\n return dirHandleRes.andThenAsync(\n async dirHandle => {\n // currently only rely on traversal inspection\n for await ( const [ name,\n handle] of dirHandle.entries() ) {\n if ( name === childName ) {\n return Ok(\n handle);\n }\n }\n const\n err =\n new Error(\n `${ NOT_FOUND_ERROR }: '${ childName }' ` +\n `does not exist. Full path is '${ path }'.`);\n err.name =\n NOT_FOUND_ERROR;\n return Err(\n err);\n });\n}\n\n/**\n * Writes content to a file at the specified path.\n *\n * @param filePath - The path of the file to write to.\n * @param contents - The content to write to the file.\n * @param options - Optional write options.\n * @returns A promise that resolves to an `AsyncIOResult` indicating whether the file was successfully written.\n */\nexport async function\n writeFile(\n filePath:\n string,\n contents:\n WriteFileContent,\n options?:\n WriteOptions):\n AsyncVoidIOResult {\n assertAbsolutePath(\n filePath);\n // create as default\n const { append = false, create = true } = options ?? {};\n const\n fileHandleRes =\n await getFileHandle(\n filePath,\n { create });\n return fileHandleRes.andThenAsync(\n async fileHandle => {\n const\n writable =\n await fileHandle.createWritable(\n { keepExistingData:\n append });\n const\n params:\n WriteParams =\n { type:\n 'write',\n data:\n contents,\n };\n // append?\n if ( append ) {\n const\n { size } =\n await fileHandle.getFile();\n params.position =\n size;\n }\n await writable.write(\n params);\n await writable.close();\n return RESULT_VOID;\n });\n}\n","// SPDX-License-Identifier: GPL-3.0-or-later\n\n/** ----------------------------------------------------------------------\n * Copyright ©\n * Jiang Jie\n * 2024, 2025\n * Pellegrino Prevete\n * 2025\n * \n * All rights reserved\n * ----------------------------------------------------------------------\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n * \n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { fetchT,\n type FetchResponse,\n type FetchTask } from '@happy-ts/fetch-t';\nimport { extname } from '@std/path/posix';\nimport { Err,\n Ok } from 'happy-rusty';\nimport { assertAbsolutePath,\n assertFileUrl } from './assertions.ts';\nimport type { DownloadFileTempResponse,\n FsRequestInit } from './defines.ts';\nimport { createAbortError } from './helpers.ts';\nimport { writeFile } from './opfs_core.ts';\nimport { generateTempPath } from './utils.ts';\n\n/**\n * Downloads a file from a URL and saves it to a temporary file.\n * The returned response will contain the temporary file path.\n *\n * @param fileUrl - The URL of the file to download.\n * @param requestInit - Optional request initialization parameters.\n * @returns A task that can be aborted and contains the result of the download.\n */\nexport function\n downloadFile(\n fileUrl:\n string,\n requestInit?:\n FsRequestInit):\n FetchTask<DownloadFileTempResponse>;\n\n/**\n * Downloads a file from a URL and saves it to the specified path.\n *\n * @param fileUrl - The URL of the file to download.\n * @param filePath - The path where the downloaded file will be saved.\n * @param requestInit - Optional request initialization parameters.\n * @returns A task that can be aborted and contains the result of the download.\n */\nexport function\n downloadFile(\n fileUrl:\n string,\n filePath:\n string,\n requestInit?:\n FsRequestInit):\n FetchTask<Response>;\nexport function\n downloadFile(\n fileUrl:\n string,\n filePath?:\n string | FsRequestInit,\n requestInit?:\n FsRequestInit):\n FetchTask<Response | DownloadFileTempResponse> {\n let\n aborted:\n boolean,\n saveToTemp;\n assertFileUrl(\n fileUrl);\n saveToTemp =\n false;\n if ( typeof filePath === 'string' ) {\n assertAbsolutePath(\n filePath);\n }\n else {\n requestInit =\n filePath;\n // save to a temporary file, reserve the extension\n filePath =\n generateTempPath(\n { extname:\n extname(\n fileUrl) });\n saveToTemp =\n true;\n }\n aborted =\n false;\n const\n fetchTask =\n fetchT(\n fileUrl,\n { redirect:\n 'follow',\n ...requestInit,\n abortable:\n true });\n const\n response =\n ( async ():\n FetchResponse<Response> => {\n const\n responseRes =\n await fetchTask.response;\n return responseRes.andThenAsync(\n async response => {\n const\n blob =\n await response.blob();\n // maybe aborted\n if ( aborted ) {\n return Err(\n createAbortError());\n }\n const\n writeRes =\n await writeFile(filePath, blob);\n return writeRes.and(\n Ok(response));\n });\n })();\n return {\n abort(\n reason?:\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any):\n void {\n aborted =\n true;\n fetchTask.abort(\n reason);\n },\n get aborted():\n boolean {\n return aborted;\n },\n get response():\n FetchResponse<Response | DownloadFileTempResponse> {\n return saveToTemp ?\n response.then(\n res => {\n return res.map<DownloadFileTempResponse>(\n rawResponse => {\n return {\n tempFilePath:\n filePath,\n rawResponse,\n };\n });\n }) :\n response;\n },\n };\n}\n","// SPDX-License-Identifier: GPL-3.0-or-later\n\n/** ----------------------------------------------------------------------\n * Copyright ©\n * Jiang Jie\n * 2024, 2025\n * Pellegrino Prevete\n * 2025\n * \n * All rights reserved\n * ----------------------------------------------------------------------\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n * \n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { basename,\n dirname,\n join } from '@std/path/posix';\nimport { Err,\n Ok,\n RESULT_FALSE,\n RESULT_VOID,\n type AsyncIOResult,\n type AsyncVoidIOResult,\n type IOResult } from 'happy-rusty';\nimport invariant from 'tiny-invariant';\nimport { assertAbsolutePath } from './assertions.ts';\nimport type { CopyOptions,\n ExistsOptions,\n MoveOptions,\n WriteFileContent } from './defines.ts';\nimport { getDirHandle,\n getFinalResult,\n isNotFoundError } from './helpers.ts';\nimport { mkdir,\n readDir,\n readFile,\n remove,\n stat,\n writeFile } from './opfs_core.ts';\nimport { isDirectoryHandle,\n isFileHandle } from './utils.ts';\n\n/**\n * Moves a file handle to a new path.\n *\n * @param fileHandle - The file handle to move.\n * @param newPath - The new path of the file handle.\n * @returns A promise that resolves to an `AsyncVoidIOResult`\n * indicating whether the file handle was successfully moved.\n */\nasync function\n moveHandle(\n fileHandle:\n FileSystemFileHandle,\n newPath:\n string):\n AsyncVoidIOResult {\n const\n newDirPath =\n dirname(\n newPath);\n return (\n await getDirHandle(\n newDirPath,\n { create:\n true })).andThenAsync(\n async newDirHandle => {\n const\n newName =\n basename(\n newPath);\n try {\n // TODO ts not support yet\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await ( fileHandle as any ).move(\n newDirHandle,\n newName);\n return RESULT_VOID;\n }\n\t catch (\n _error) {\n return Err(\n _error as DOMException);\n }\n });\n}\n\n/**\n * @param srcFileHandle - The source file handle to move or copy.\n * @param destFilePath - The destination file path.\n */\ntype handleSrcFileToDest =\n ( srcFileHandle:\n FileSystemFileHandle,\n destFilePath:\n string ) => AsyncVoidIOResult;\n\n/**\n * Copy or move a file or directory from one path to another.\n * @param srcPath - The source file/directory path.\n * @param destPath - The destination file/directory path.\n * @param handler - How to handle the file handle to the destination.\n * @param overwrite - Whether to overwrite the destination file if it exists.\n * @returns A promise that resolves to an `AsyncVoidIOResult` indicating\n * whether the file was successfully copied/moved.\n */\nasync function\n mkDestFromSrc(\n srcPath:\n string,\n destPath:\n string,\n handler:\n handleSrcFileToDest,\n overwrite = true):\n AsyncVoidIOResult {\n assertAbsolutePath(\n destPath);\n return (\n await stat(\n srcPath)).andThenAsync(\n async srcHandle => {\n let\n destExists;\n // if overwrite is false,\n // we need this flag to determine\n // whether to write file.\n destExists =\n false;\n const\n destHandleRes =\n await stat(\n destPath);\n if ( destHandleRes.isErr() ) {\n if ( ! isNotFoundError(\n destHandleRes.unwrapErr()) ) {\n return destHandleRes.asErr();\n }\n }\n else {\n destExists =\n true;\n // check\n const\n destHandle =\n destHandleRes.unwrap();\n if ( ! ( ( isFileHandle(\n srcHandle) && isFileHandle(\n destHandle) ) ||\n ( isDirectoryHandle(\n srcHandle) && isDirectoryHandle(\n destHandle) ) ) ) {\n return Err(\n new Error(\n `Both 'srcPath' and 'destPath' ` +\n `must both be a file or directory.`));\n }\n }\n // both are files\n if ( isFileHandle(\n srcHandle)) {\n return ( overwrite ||\n ! destExists) ? await handler(\n srcHandle,\n destPath) : RESULT_VOID;\n }\n // both are directories\n const\n readDirRes =\n await readDir(\n srcPath,\n { recursive:\n true });\n return readDirRes.andThenAsync(\n async entries => {\n const\n tasks:\n AsyncVoidIOResult[] = [\n // make sure new dir created\n mkdir(\n destPath),\n ];\n for await (const { path,\n handle } of entries) {\n const\n newEntryPath =\n join(\n destPath,\n path);\n let\n newPathExists =\n false;\n if ( destExists ) {\n // should check every file\n const existsRes =\n await exists(\n newEntryPath);\n if ( existsRes.isErr() ) {\n tasks.push(\n Promise.resolve(\n existsRes.asErr()));\n continue;\n }\n newPathExists =\n existsRes.unwrap();\n }\n const\n res:\n AsyncVoidIOResult =\n isFileHandle(\n handle) ?\n ( overwrite ||\n ! newPathExists ?\n handler(\n handle,\n newEntryPath) :\n Promise.resolve(\n RESULT_VOID) ) :\n mkdir(\n newEntryPath);\n tasks.push(\n res);\n }\n return getFinalResult(\n tasks);\n });\n });\n}\n\n/**\n * Appends content to a file at the specified path.\n *\n * @param filePath - The path of the file to append to.\n * @param contents - The content to append to the file.\n * @returns A promise that resolves to an `AsyncIOResult`\n * indicating whether the content was successfully appended.\n */\nexport\n function\n appendFile(\n filePath:\n string,\n contents:\n WriteFileContent):\n AsyncVoidIOResult {\n return writeFile(\n filePath,\n contents,\n { append:\n true,\n });\n}\n\n/**\n * Copies a file or directory from one location to another same as `cp -r`.\n *\n * Both `srcPath` and `destPath` must both be a file or directory.\n *\n * @param srcPath - The source file/directory path.\n * @param destPath - The destination file/directory path.\n * @param options - The copy options.\n * @returns A promise that resolves to an `AsyncVoidIOResult`\n * indicating whether the file was successfully copied.\n */\nexport\n async function\n copy(\n srcPath:\n string,\n destPath:\n string,\n options?:\n CopyOptions):\n AsyncVoidIOResult {\n const\n { overwrite = true } =\n options ?? {};\n\n return mkDestFromSrc(\n srcPath,\n destPath,\n async (\n srcHandle:\n FileSystemFileHandle,\n destPath:\n string) => {\n return await writeFile(\n destPath,\n await srcHandle.getFile());\n },\n overwrite);\n}\n\n/**\n * Empties the contents of a directory at the specified path.\n *\n * @param dirPath - The path of the directory to empty.\n * @returns A promise that resolves to an `AsyncIOResult`\n * indicating whether the directory was successfully emptied.\n */\nexport\n async function\n emptyDir(\n dirPath:\n string):\n AsyncVoidIOResult {\n const\n readDirRes =\n await\n readDir(\n dirPath);\n if ( readDirRes.isErr() ) {\n // create if not exist\n return isNotFoundError(\n readDirRes.unwrapErr()) ?\n mkdir(\n dirPath) :\n readDirRes.asErr();\n }\n const\n tasks:\n AsyncVoidIOResult[] =\n [];\n for await ( const { path } of readDirRes.unwrap() ) {\n tasks.push(\n remove(\n join(\n dirPath,\n path)));\n }\n return getFinalResult(\n tasks);\n}\n\n/**\n * Checks whether a file or directory exists at the specified path.\n *\n * @param path - The path of the file or directory to check for existence.\n * @param options - Optional existence options.\n * @returns A promise that resolves to an `AsyncIOResult`\n * indicating whether the file or directory exists.\n */\nexport\n async function\n exists(\n path:\n string,\n options?:\n ExistsOptions):\n AsyncIOResult<boolean> {\n const\n { isDirectory = false,\n isFile = false } =\n options ??\n {};\n invariant(\n ! ( isDirectory &&\n isFile),\n () => 'ExistsOptions.isDirectory and ' +\n 'ExistsOptions.isFile must not ' +\n\t\t 'be true together.');\n const\n statRes =\n await stat(\n path);\n return statRes.andThen(\n handle => {\n const\n notExist =\n ( isDirectory &&\n isFileHandle(\n handle) ) ||\n ( isFile &&\n isDirectoryHandle(\n handle) );\n return Ok(\n ! notExist);\n }).orElse(\n (_error):\n IOResult<boolean> => {\n return isNotFoundError(\n _error) ?\n RESULT_FALSE :\n statRes.asErr();\n });\n}\n\n/**\n * Move a file or directory from an old path to a new path.\n *\n * @param srcPath - The current path of the file or directory.\n * @param destPath - The new path of the file or directory.\n * @param options - Options of move.\n * @returns A promise that resolves to an `AsyncIOResult`\n * indicating whether the file or directory was\n * successfully moved.\n */\nexport\n async function\n move(\n srcPath:\n string,\n destPath:\n string,\n options?:\n MoveOptions):\n AsyncVoidIOResult {\n const\n { overwrite = true } =\n options ??\n {};\n return (\n await mkDestFromSrc(\n srcPath,\n destPath,\n moveHandle,\n overwrite) ).andThenAsync(\n () => {\n // finally remove src\n return remove(\n srcPath);\n });\n}\n\n/**\n * Reads the content of a file at the specified path as a File.\n *\n * @param filePath - The path of the file to read.\n * @returns A promise that resolves to an `AsyncIOResult`\n * containing the file content as a File.\n */\nexport\n function\n readBlobFile(\n filePath:\n string):\n AsyncIOResult<File> {\n return readFile(\n filePath,\n { encoding:\n 'blob' });\n}\n\n/**\n * Reads the content of a file at the specified path as a\n * string and returns it as a JSON object.\n *\n * @param filePath - The path of the file to read.\n * @returns A promise that resolves to an `AsyncIOResult`\n * containing the file content as a JSON object.\n */\nexport\n async function\n readJsonFile<T>(\n filePath:\n string):\n AsyncIOResult<T> {\n return (\n await readTextFile(\n filePath)).andThenAsync(\n async contents => {\n try {\n return Ok(\n JSON.parse(\n contents));\n }\n\t catch (\n _error) {\n return Err(\n _error as Error);\n }\n });\n}\n\n/**\n * Reads the content of a file at the specified path as a string.\n *\n * @param filePath - The path of the file to read.\n * @returns A promise that resolves to an `AsyncIOResult`\n * containing the file content as a string.\n */\nexport\n function\n readTextFile(\n filePath:\n string):\n AsyncIOResult<string> {\n return readFile(\n filePath,\n { encoding:\n 'utf8' });\n}\n","// SPDX-License-Identifier: GPL-3.0-or-later\n\n/** ----------------------------------------------------------------------\n * Copyright ©\n * Jiang Jie\n * 2024, 2025\n * Pellegrino Prevete\n * 2025\n * \n * All rights reserved\n * ----------------------------------------------------------------------\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n * \n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { Err,\n Ok,\n RESULT_VOID,\n type AsyncIOResult,\n type AsyncVoidIOResult } from 'happy-rusty';\nimport invariant from 'tiny-invariant';\nimport { TMP_DIR } from './constants.ts';\nimport type { TempOptions } from './defines.ts';\nimport { createFile,\n mkdir,\n readDir,\n remove } from './opfs_core.ts';\nimport { generateTempPath,\n isFileHandle } from './utils.ts';\n\n/**\n * Create a temporary file or directory.\n *\n * @param options - Options and flags.\n * @returns A promise that resolves the result of\n * the temporary file or directory path.\n */\nexport\n async function\n mkTemp(\n options?:\n TempOptions):\n AsyncIOResult<string> {\n const\n { isDirectory = false } =\n options ??\n {};\n const\n path =\n generateTempPath(\n options);\n const\n res =\n await ( isDirectory ?\n mkdir :\n createFile)(\n path);\n return res.and(\n Ok(path));\n}\n\n/**\n * Delete the temporary directory and all its contents.\n * @returns A promise that resolves to an `AsyncVoidIOResult`\n * indicating whether the temporary directory was\n * successfully deleted.\n */\nexport\n function\n deleteTemp():\n AsyncVoidIOResult {\n return remove(\n TMP_DIR);\n}\n\n/**\n * Prune the temporary directory and delete all expired files.\n * @param expired - The date to determine whether a file is expired.\n * @returns A promise that resolves to an `AsyncVoidIOResult`\n * indicating whether the temporary directory was successfully pruned.\n */\nexport\n async function\n pruneTemp(\n expired:\n Date):\n AsyncVoidIOResult {\n invariant(\n expired instanceof Date,\n\t() => `Expired must be a Date` +\n `but received ${ expired }`);\n const\n readDirRes =\n await readDir(\n TMP_DIR,\n { recursive:\n true,\n });\n return readDirRes.andThenAsync(\n async entries => {\n try {\n for await ( const { handle } of entries ) {\n if (\n isFileHandle(\n handle) &&\n ( await handle.getFile() ).lastModified <= expired.getTime() ) {\n // TODO ts not support yet\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await ( handle as any ).remove();\n }\n }\n }\n\tcatch (\n _error) {\n return Err(\n _error as DOMException);\n }\n return RESULT_VOID;\n });\n}\n","// SPDX-License-Identifier: GPL-3.0-or-later\n\n/** ----------------------------------------------------------------------\n * Copyright ©\n * Jiang Jie\n * 2024, 2025\n * Pellegrino Prevete\n * 2025\n * \n * All rights reserved\n * ----------------------------------------------------------------------\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n * \n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { fetchT } from '@happy-ts/fetch-t';\nimport { join,\n SEPARATOR } from '@std/path/posix';\nimport * as fflate from 'fflate/browser';\nimport { Err,\n type AsyncVoidIOResult,\n type VoidIOResult } from 'happy-rusty';\nimport { Future } from 'tiny-future';\nimport { assertAbsolutePath,\n assertFileUrl } from './assertions.ts';\nimport type { FsRequestInit } from './defines.ts';\nimport { getFinalResult } from './helpers.ts';\nimport { readFile,\n writeFile } from './opfs_core.ts';\n\n/**\n * Unzip a buffer then write to the target path.\n * @param buffer - Zipped ArrayBuffer.\n * @param targetPath - Target directory path.\n */\nasync function\n unzipBufferToTarget(\n buffer:\n ArrayBuffer,\n targetPath:\n string):\n AsyncVoidIOResult {\n const\n data =\n new Uint8Array(\n buffer);\n const\n future =\n new Future<VoidIOResult>();\n fflate.unzip(\n data,\n async (err, unzipped) => {\n if (err) {\n future.resolve(\n Err(\n err));\n return;\n }\n const\n tasks:\n AsyncVoidIOResult[] =\n [];\n for ( const path in unzipped ) {\n // ignore directory\n if ( path.at(\n -1) !== SEPARATOR ) {\n tasks.push(\n writeFile(\n join(\n targetPath,\n path),\n unzipped[\n path]));\n }\n }\n future.resolve(\n getFinalResult(\n tasks));\n });\n return await future.promise;\n}\n\n/**\n * Unzip a zip file to a directory.\n * Equivalent to `unzip -o <zipFilePath> -d <targetPath>\n *\n * Usees [fflate](https://github.com/101arrowz/fflate) as the unzip backend.\n * @param zipFilePath - Zip file path.\n * @param targetPath - The directory to unzip to.\n * @returns A promise that resolves to an `AsyncIOResult`\n * indicating whether the zip file was successfully unzipped.\n */\nexport\n async function\n unzip(\n zipFilePath:\n string,\n targetPath:\n string):\n AsyncVoidIOResult {\n assertAbsolutePath(\n targetPath);\n const\n fileRes =\n await readFile(\n zipFilePath);\n return fileRes.andThenAsync(\n buffer => {\n return unzipBufferToTarget(\n buffer,\n targetPath);\n });\n}\n\n/**\n * Unzip a remote zip file to a directory.\n * Equivalent to `unzip -o <zipFilePath> -d <targetPath>\n *\n * Use [fflate](https://github.com/101arrowz/fflate) as the unzip backend.\n * @param zipFileUrl - Zip file url.\n * @param targetPath - The directory to unzip to.\n * @param requestInit - Optional request initialization parameters.\n * @returns A promise that resolves to an `AsyncIOResult`\n * indicating whether the zip file was successfully unzipped.\n */\nexport\n async function\n unzipFromUrl(\n zipFileUrl:\n string,\n targetPath:\n string,\n requestInit?:\n FsRequestInit):\n AsyncVoidIOResult {\n assertFileUrl(\n zipFileUrl);\n assertAbsolutePath(\n targetPath);\n const\n fetchRes =\n await fetchT(\n zipFileUrl,\n { redirect:\n 'follow',\n ...requestInit,\n responseType:\n 'arraybuffer',\n abortable:\n false });\n return fetchRes.andThenAsync(\n buffer => {\n return unzipBufferToTarget(\n buffer,\n targetPath);\n });\n}\n","// SPDX-License-Identifier: GPL-3.0-or-later\n\n/** ----------------------------------------------------------------------\n * Copyright ©\n * Jiang Jie\n * 2024, 2025\n * Pellegrino Prevete\n * 2025\n * \n * All rights reserved\n * ----------------------------------------------------------------------\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n * \n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { fetchT,\n type FetchResponse,\n\t type FetchTask } from '@happy-ts/fetch-t';\nimport { basename } from '@std/path/posix';\nimport { Err } from 'happy-rusty';\nimport { assertFileUrl } from './assertions.ts';\nimport type { UploadRequestInit } from './defines.ts';\nimport { createAbortError } from './helpers.ts';\nimport { readBlobFile } from './opfs_ext.ts';\n\n/**\n * Uploads a file from the specified path to a URL.\n *\n * @param filePath - The path of the file to upload.\n * @param fileUrl - The URL where the file will be uploaded.\n * @param requestInit - Optional request initialization parameters.\n * @returns A promise that resolves to an `AsyncIOResult`\n * indicating whether the file was successfully uploaded.\n */\nexport function\n uploadFile(\n filePath:\n string,\n fileUrl:\n string,\n requestInit?:\n UploadRequestInit):\n FetchTask<Response> {\n type T =\n Response;\n let\n aborted:\n boolean;\n let\n fetchTask:\n FetchTask<T>;\n assertFileUrl(\n fileUrl);\n aborted =\n false;\n const\n response =\n ( async ():\n FetchResponse<Response> => {\n const\n fileRes =\n await readBlobFile(\n filePath);\n return fileRes.andThenAsync(\n async file => {\n // maybe aborted\n if ( aborted ) {\n return Err(\n createAbortError());\n }\n const\n { // default file name\n filename = basename(filePath),\n ...rest } =\n requestInit ??\n {};\n const\n formData =\n new FormData();\n formData.append(\n filename,\n file,\n filename);\n fetchTask =\n fetchT(\n fileUrl,\n { method:\n 'POST',\n ...rest,\n abortable:\n true,\n body:\n formData });\n return fetchTask.response;\n });\n })();\n return {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n abort(reason?: any):\n void {\n aborted =\n true;\n fetchTask?.abort(\n reason);\n },\n get aborted():\n boolean {\n return aborted;\n },\n get response():\n FetchResponse<Response> {\n return response;\n },\n };\n}\n","// SPDX-License-Identifier: GPL-3.0-or-later\n\n/** ----------------------------------------------------------------------\n * Copyright ©\n * Jiang Jie\n * 2024, 2025\n * Pellegrino Prevete\n * 2025\n * \n * All rights reserved\n * ----------------------------------------------------------------------\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n * \n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { fetchT } from '@happy-ts/fetch-t';\nimport { basename,\n join } from '@std/path/posix';\nimport * as fflate from 'fflate/browser';\nimport { Err,\n Ok,\n type AsyncIOResult,\n type AsyncVoidIOResult,\n type IOResult } from 'happy-rusty';\nimport { Future } from 'tiny-future';\nimport { assertAbsolutePath,\n assertFileUrl } from './assertions.ts';\nimport type { FsRequestInit,\n ZipOptions } from './defines.ts';\nimport { readDir,\n stat,\n writeFile } from './opfs_core.ts';\nimport { getFileDataByHandle,\n isFileHandle } from './utils.ts';\n\n/**\n * Zip a zippable data then write to the target path.\n * @param zippable - Zippable data.\n * @param zipFilePath - Target zip file path.\n */\nasync function\n zipTo<T>(\n zippable:\n fflate.AsyncZippable,\n zipFilePath?:\n string):\n AsyncIOResult<T> {\n const\n future =\n new Future<IOResult<T>>();\n fflate.zip(\n zippable,\n { consume:\n true },\n async (err,\n u8a) => {\n if ( err ) {\n future.resolve(\n Err(\n err));\n return;\n }\n // whether to write to file\n if ( zipFilePath ) {\n const\n res =\n await writeFile(\n zipFilePath,\n u8a);\n future.resolve(\n res as IOResult<T>);\n }\n else {\n future.resolve(\n Ok( u8a as T ));\n }\n });\n return await future.promise;\n}\n\n/**\n * Zip a file or directory and write to a zip file.\n * Equivalent to `zip -r <zipFilePath> <targetPath>`.\n *\n * Use [fflate](https://github.com/101arrowz/fflate) as the zip backend.\n * @param sourcePath - The path to be zipped.\n * @param zipFilePath - The path to the zip file.\n * @param options - Options of zip.\n * @returns A promise that resolves to an `AsyncIOResult`\n * indicating whether the source was successfully zipped.\n */\nexport\n async function\n zip(\n sourcePath:\n string,\n zipFilePath:\n string,\n options?:\n ZipOptions):\n AsyncVoidIOResult;\n\n/**\n * Zip a file or directory and return the zip file data.\n * Equivalent to `zip -r <zipFilePath> <targetPath>`.\n *\n * Use [fflate](https://github.com/101arrowz/fflate) as the zip backend.\n * @param sourcePath - The path to be zipped.\n * @param options - Options of zip.\n * @returns A promise that resolves to an `AsyncIOResult` indicating\n * whether the source was successfully zipped.\n */\nexport\n async function\n zip(\n sourcePath:\n string,\n options?:\n ZipOptions):\n AsyncIOResult<Uint8Array>;\nexport\n async function\n zip<T>(\n sourcePath:\n string,\n zipFilePath?:\n string |\n ZipOptions,\n options?:\n ZipOptions):\n AsyncIOResult<T> {\n if ( typeof zipFilePath === 'string' ) {\n assertAbsolutePath(\n zipFilePath);\n }\n else {\n options =\n zipFilePath;\n zipFilePath =\n undefined;\n }\n const\n statRes =\n await stat(\n sourcePath);\n return statRes.andThenAsync(\n async handle => {\n const\n sourceName =\n basename(\n sourcePath);\n const\n zippable:\n fflate.AsyncZippable =\n {};\n if ( isFileHandle(\n handle) ) {\n // file\n const\n data =\n await getFileDataByHandle(\n handle);\n zippable[\n sourceName] =\n data;\n }\n\telse {\n // directory\n const\n readDirRes =\n await readDir(\n sourcePath,\n { recursive:\n true });\n if ( readDirRes.isErr() ) {\n return readDirRes.asErr();\n }\n // default to preserve root\n const\n preserveRoot =\n options?.preserveRoot ??\n true;\n for await ( const { path,\n handle } of readDirRes.unwrap()) {\n // path\n if ( isFileHandle(\n handle) ) {\n const\n entryName =\n preserveRoot ?\n join(\n sourceName,\n path) :\n path;\n const\n data =\n await getFileDataByHandle(\n handle);\n zippable[\n entryName] =\n data;\n }\n }\n }\n return zipTo(\n zippable,\n zipFilePath);\n });\n}\n\n/**\n * Zip a remote file and write to a zip file.\n *\n * Use [fflate](https://github.com/101arrowz/fflate) as the zip backend.\n * @param sourceUrl - The url to be zipped.\n * @param zipFilePath - The path to the zip file.\n * @param requestInit - Optional request initialization parameters.\n * @returns A promise that resolves to an `AsyncIOResult`\n * indicating whether the source was successfully zipped.\n */\nexport\n async function\n zipFromUrl(\n sourceUrl:\n string,\n zipFilePath:\n string,\n requestInit?:\n FsRequestInit):\n AsyncVoidIOResult;\n\n/**\n * Zip a remote file and return the zip file data.\n *\n * Use [fflate](https://github.com/101arrowz/fflate) as the zip backend.\n * @param sourceUrl - The url to be zipped.\n * @param requestInit - Optional request initialization parameters.\n * @returns A promise that resolves to an `AsyncIOResult` indicating\n * whether the source was successfully zipped.\n */\nexport\n async function\n zipFromUrl(\n sourceUrl:\n string,\n requestInit?:\n FsRequestInit):\n AsyncIOResult<Uint8Array>;\n\nexport\n async function\n zipFromUrl<T>(\n sourceUrl:\n string,\n zipFilePath?:\n string |\n FsRequestInit,\n requestInit?:\n FsRequestInit):\n AsyncIOResult<T> {\n assertFileUrl(\n sourceUrl);\n if ( typeof zipFilePath === 'string' ) {\n assertAbsolutePath(\n zipFilePath);\n }\n else {\n requestInit =\n zipFilePath;\n zipFilePath =\n undefined;\n }\n const\n fetchRes =\n await fetchT(\n sourceUrl,\n { redirect:\n 'follow',\n ...requestInit,\n responseType:\n 'arraybuffer',\n abortable:\n false,\n });\n return fetchRes.andThenAsync(\n buffer => {\n const\n sourceName =\n basename(\n sourceUrl);\n const\n zippable:\n fflate.AsyncZippable =\n {};\n zippable[\n sourceName] =\n new Uint8Array(\n buffer);\n return zipTo(\n zippable,\n zipFilePath);\n });\n}\n","// SPDX-License-Identifier: GPL-3.0-or-later\n\n/** ----------------------------------------------------------------------\n * Copyright ©\n * Jiang Jie\n * 2024, 2025\n * Pellegrino Prevete\n * 2025\n * \n * All rights reserved\n * ----------------------------------------------------------------------\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n * \n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\n/**\n * Checks if the Origin Private File System (OPFS) is supported in the current environment.\n *\n * @returns A boolean indicating whether OPFS is supported.\n */\nexport function\n isOPFSSupported():\n boolean {\n return typeof navigator?.storage?.getDirectory === 'function';\n}\n","// SPDX-License-Identifier: GPL-3.0-or-later\n\n/** ----------------------------------------------------------------------\n * Copyright ©\n * Jiang Jie\n * 2024, 2025\n * Pellegrino Prevete\n * 2025\n * \n * All rights reserved\n * ----------------------------------------------------------------------\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n * \n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { join,\n SEPARATOR } from '@std/path/posix';\nimport { TMP_DIR } from './constants.ts';\nimport type { FileSystemFileHandleLike,\n FileSystemHandleLike,\n TempOptions } from './defines.ts';\n\n/**\n * Generate a temporary path but not create it.\n *\n * @param options - Options and flags.\n * @returns The temporary path.\n */\nexport function\n generateTempPath(\n options?:\n TempOptions):\n string {\n const\n { isDirectory = false,\n basename = 'tmp',\n extname = '' } = options ?? {};\n const\n base =\n basename ? `${ basename }-` : '';\n const\n ext =\n isDirectory ? '' : extname;\n // use uuid to generate a unique name\n return join(\n TMP_DIR,\n `${ base }${ crypto.randomUUID() }${ ext }`);\n}\n\n/**\n * Check whether the path is a temporary path.\n * @param path - The path to check.\n * @returns `true` if the path is a temporary path otherwise `false`.\n */\nexport function\n isTempPath(\n path:\n string):\n boolean {\n return path.startsWith(\n `${ TMP_DIR }${ SEPARATOR }`);\n}\n\n/**\n * Serialize a `FileSystemHandle` to plain object.\n * @param handle - `FileSystemHandle` object.\n * @returns Serializable version of FileSystemHandle that is FileSystemHandleLike.\n */\nexport async function\n toFileSystemHandleLike(\n handle:\n FileSystemHandle):\n Promise<FileSystemHandleLike> {\n const\n { name,\n kind } =\n handle;\n if ( isFileHandle(\n handle) ) {\n const\n file =\n await handle.getFile();\n const\n { size,\n lastModified,\n\t type } =\n file;\n const\n fileHandle:\n FileSystemFileHandleLike = {\n name,\n kind,\n type,\n size,\n lastModified,\n };\n return fileHandle;\n }\n const\n handleLike:\n FileSystemHandleLike= {\n name,\n kind,\n };\n return handleLike;\n}\n\n/**\n * Whether the handle is a file.\n * @param handle - The handle which is a FileSystemHandle.\n * @returns `true` if the handle is a file, otherwise `false`.\n */\nexport function\n isFileHandle(\n handle:\n FileSystemHandle):\n handle is FileSystemFileHandle {\n return handle.kind === 'file';\n}\n\n/**\n * Whether the handle is a directory.\n * @param handle - The handle which is a FileSystemHandle.\n * @returns `true` if the handle is a directory, otherwise `false`.\n */\nexport function\n isDirectoryHandle(\n handle:\n FileSystemHandle):\n handle is FileSystemDirectoryHandle {\n return handle.kind === 'directory';\n}\n\n/**\n * Whether the handle is a file-like.\n * @param handle - The handle which is a FileSystemHandleLike.\n * @returns `true` if the handle is a file, otherwise `false`.\n */\nexport function\n isFileHandleLike(\n handle:\n FileSystemHandleLike):\n handle is FileSystemFileHandleLike {\n return handle.kind === 'file';\n}\n\n/**\n * Gets the data of a file handle.\n * @param handle - The file handle.\n * @returns A promise that resolves to the data of the file.\n */\nexport async function\n getFileDataByHandle(\n handle:\n FileSystemFileHandle):\n Promise<Uint8Array> {\n const\n file =\n await handle.getFile();\n const\n ab =\n await file.arrayBuffer();\n return new Uint8Array(\n ab);\n}\n","// SPDX-License-Identifier: GPL-3.0-or-later\n\n/** ----------------------------------------------------------------------\n * Copyright ©\n * Jiang Jie\n * 2024, 2025\n * Pellegrino Prevete\n * 2025\n * \n * All rights reserved\n * ----------------------------------------------------------------------\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n * \n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport type { IOResult } from 'happy-rusty';\nimport type { ReadDirEntry,\n ReadDirEntrySync } from '../fs/defines.ts';\nimport { createFile,\n mkdir,\n readDir,\n remove,\n stat,\n writeFile } from '../fs/opfs_core.ts';\nimport { appendFile,\n copy,\n emptyDir,\n exists,\n move,\n readBlobFile } from '../fs/opfs_ext.ts';\nimport { deleteTemp,\n mkTemp,\n pruneTemp } from '../fs/opfs_tmp.ts';\nimport { unzip } from '../fs/opfs_unzip.ts';\nimport { zip } from '../fs/opfs_zip.ts';\nimport { toFileSystemHandleLike } from '../fs/utils.ts';\nimport { serializeError,\n serializeFile } from './helpers.ts';\nimport { decodeFromBuffer,\n encodeToBuffer,\n respondToMainFromWorker,\n SyncMessenger,\n WorkerAsyncOp } from './shared.ts';\n\n/**\n * Async I/O operations which allow to call from main thread.\n */\nconst\n asyncOps = {\n [ WorkerAsyncOp.createFile ]:\n createFile,\n [ WorkerAsyncOp.mkdir ]:\n mkdir,\n [ WorkerAsyncOp.move ]:\n move,\n [ WorkerAsyncOp.readDir ]:\n readDir,\n [ WorkerAsyncOp.remove ]:\n remove,\n [ WorkerAsyncOp.stat ]:\n stat,\n [ WorkerAsyncOp.writeFile ]:\n writeFile,\n [ WorkerAsyncOp.appendFile ]:\n appendFile,\n [ WorkerAsyncOp.copy ]:\n copy,\n [ WorkerAsyncOp.emptyDir ]:\n emptyDir,\n [ WorkerAsyncOp.exists ]:\n exists,\n [ WorkerAsyncOp.deleteTemp ]:\n deleteTemp,\n [ WorkerAsyncOp.mkTemp ]:\n mkTemp,\n [ WorkerAsyncOp.pruneTemp ]:\n pruneTemp,\n [ WorkerAsyncOp.readBlobFile ]:\n readBlobFile,\n [ WorkerAsyncOp.unzip ]:\n unzip,\n [ WorkerAsyncOp.zip ]:\n zip,\n};\n\n/**\n * Cache the messenger instance.\n */\nlet\n messenger:\n SyncMessenger;\n\n/**\n * Start worker agent.\n * Listens to postMessage from main thread.\n * Start runner loop.\n */\nexport\n function\n startSyncAgent() {\n if ( typeof window !== 'undefined' ) {\n throw new Error(\n 'Only can use in worker');\n }\n if ( messenger ) {\n throw new Error(\n 'Worker messenger already started');\n }\n addEventListener(\n 'message',\n (event:\n MessageEvent<SharedArrayBuffer>) => {\n // created at main thread and transfer to worker\n const\n sab =\n event.data;\n\n if ( ! (sab instanceof SharedArrayBuffer) ) {\n throw new TypeError(\n 'Only can post SharedArrayBuffer to Worker');\n }\n messenger =\n new SyncMessenger(sab);\n // notify main thread that worker is ready\n postMessage(\n true);\n // start waiting for request\n runWorkerLoop();\n });\n}\n\n/**\n * Run worker loop.\n */\nasync function\n runWorkerLoop():\n Promise<void> {\n // loop forever\n while ( true ) {\n try {\n await respondToMainFromWorker(\n messenger,\n async (data) => {\n const\n [op, ...args] =\n decodeFromBuffer(\n data) as [ WorkerAsyncOp,\n ...Parameters<typeof asyncOps[\n WorkerAsyncOp]> ];\n // handling unequal parameters for serialization and deserialization\n if ( op === WorkerAsyncOp.writeFile ||\n op === WorkerAsyncOp.appendFile) {\n // actually is an byte array\n if ( Array.isArray(\n args[\n 1]) ) {\n args[\n 1] =\n new Uint8Array(\n args[1]);\n }\n }\n else if ( op === WorkerAsyncOp.pruneTemp ) {\n // actually is a Date string\n args[\n 0] =\n new Date(\n args[\n 0] as Date);\n }\n let\n response:\n Uint8Array;\n const\n handle =\n asyncOps[\n op];\n try {\n // The linter shows not to work well here\n // as it cant disable a block of lines.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const res: IOResult<any> = await ( handle as any )(\n ...args);\n if ( res.isErr() ) {\n // without result success\n response =\n encodeToBuffer(\n [ serializeError(\n res.unwrapErr()) ]);\n }\n else {\n // manually serialize response\n let\n rawResponse;\n if ( op === WorkerAsyncOp.readBlobFile ) {\n const\n file:\n File =\n res.unwrap();\n const\n fileLike =\n await serializeFile(\n file);\n rawResponse =\n { ...fileLike,\n // for serialize\n data:\n [ ...new Uint8Array(\n fileLike.data ) ] };\n }\n else if ( op === WorkerAsyncOp.readDir ) {\n const\n iterator:\n AsyncIterableIterator<ReadDirEntry> =\n res.unwrap();\n const\n entries:\n ReadDirEntrySync[] =\n [];\n for await ( const { path,\n handle } of iterator) {\n const\n handleLike =\n await toFileSystemHandleLike(\n handle);\n entries.push(\n { path,\n handle:\n handleLike });\n }\n rawResponse =\n entries;\n }\n else if ( op === WorkerAsyncOp.stat ) {\n const\n handle:\n FileSystemHandle =\n res.unwrap();\n const\n data =\n await toFileSystemHandleLike(\n handle);\n rawResponse =\n data;\n }\n else if ( op === WorkerAsyncOp.zip ) {\n const\n data:\n Uint8Array |\n undefined =\n res.unwrap();\n rawResponse =\n data instanceof Uint8Array ?\n [ ...data ] :\n data;\n }\n else {\n // others are all boolean\n rawResponse =\n res.unwrap();\n }\n // without error\n response =\n encodeToBuffer(\n [ null,\n rawResponse ]);\n }\n }\n catch (\n _error) {\n response =\n encodeToBuffer(\n [ serializeError(\n _error as Error) ]);\n }\n return response;\n });\n }\n catch (\n _error) {\n console.error(\n _error instanceof Error ?\n _error.stack :\n _error);\n }\n }\n}\n","// SPDX-License-Identifier: GPL-3.0-or-later\n\n/** ----------------------------------------------------------------------\n * Copyright ©\n * Jiang Jie\n * 2024, 2025\n * Pellegrino Prevete\n * 2025\n * \n * All rights reserved\n * ----------------------------------------------------------------------\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n * \n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { sleepUntil } from './helpers.ts';\n\n/**\n * Async I/O operations called from main thread to worker thread.\n */\nexport\n const\n enum WorkerAsyncOp {\n // core\n createFile,\n mkdir,\n move,\n readDir,\n remove,\n stat,\n writeFile,\n // ext\n appendFile,\n copy,\n emptyDir,\n exists,\n deleteTemp,\n mkTemp,\n pruneTemp,\n readBlobFile,\n unzip,\n zip,\n}\n\n/**\n * Main thread lock index used in Int32Array.\n */\nconst\n MAIN_LOCK_INDEX =\n 0;\n\n/**\n * Worker thread lock index used in Int32Array.\n */\nconst\n WORKER_LOCK_INDEX =\n 1;\n\n/**\n * Data index used in Int32Array.\n */\nconst\n DATA_INDEX =\n 2;\n\n/**\n * Main thread locked value.\n */\nconst\n MAIN_LOCKED =\n 1;\n\n/**\n * Main thread unlocked value.\n * Default.\n */\nconst\n MAIN_UNLOCKED =\n 0;\n\n/**\n * Worker thread locked value.\n * Default.\n */\nconst\n WORKER_LOCKED =\n MAIN_UNLOCKED;\n\n/**\n * Worker thread unlocked value.\n */\nconst\n WORKER_UNLOCKED =\n MAIN_LOCKED;\n\n/**\n * Cache the `TextEncoder` instance.\n */\nlet\n encoder:\n TextEncoder;\n\n/**\n * Cache the `TextDecoder` instance.\n */\nlet\n decoder:\n TextDecoder;\n\n/**\n * Get the cached `TextEncoder` instance.\n * @returns Instance of `TextEncoder`.\n */\nfunction\n getEncoder():\n TextEncoder {\n encoder ??=\n new TextEncoder();\n return encoder;\n}\n\n/**\n * Get the cached `TextDecoder` instance.\n * @returns Instance of `TextDecoder`.\n */\nfunction\n getDecoder():\n TextDecoder {\n decoder ??=\n new TextDecoder();\n return decoder;\n}\n\n/**\n * Used to encode parameters and return values.\n * @param data - Any data to encode.\n * @returns Encoded binary data.\n */\nexport\n function\n encodeToBuffer<T>(\n data:\n T):\n Uint8Array {\n const\n str =\n JSON.stringify(data);\n return getEncoder().encode(\n str);\n}\n\n/**\n * Used to decode parameters and return values.\n * @param data - Binary data to decode.\n * @returns Decoded data.\n */\nexport\n function\n decodeFromBuffer<T>(\n data:\n Uint8Array):\n T {\n const\n str =\n decodeToString(\n data);\n return JSON.parse(\n str);\n}\n\n/**\n * Commonly decode binary data to string.\n * @param data - Binary data to decode.\n * @returns Decoded string.\n */\nexport\n function\n decodeToString(\n data:\n Uint8Array):\n string {\n return getDecoder().decode(\n data);\n}\n\n/**\n * Inspired by [memfs](https://github.com/streamich/memfs/blob/master/src/fsa-to-node/worker/SyncMessenger.ts).\n *\n * Used both in main thread and worker thread.\n */\nexport\n class SyncMessenger {\n // View of SharedArrayBuffer, used to communicate between main thread and worker.\n readonly i32a:\n Int32Array;\n // View of the same SharedArrayBuffer, used to read and write binary data.\n readonly u8a:\n Uint8Array;\n // 4 int: MAIN_LOCK_INDEX WORKER_LOCK_INDEX DATA_INDEX NOT_USE\n readonly headerLength =\n 4 * 4;\n // maximum length of data to be sent. If data is longer than this, it will throw an error.\n readonly maxDataLength:\n number;\n constructor(\n sab:\n SharedArrayBuffer) {\n this.i32a =\n new Int32Array(\n sab);\n this.u8a =\n new Uint8Array(\n sab);\n this.maxDataLength =\n sab.byteLength -\n this.headerLength;\n }\n}\n\n/**\n * Calls a function in worker thread from main tread and returns the result.\n * Used in main thread.\n * @param messenger - SyncMessenger\n * @param data - Request buffer which is encoded parameters.\n * @returns - Response buffer which is encoded return value.\n */\nexport\n function\n callWorkerFromMain(\n messenger:\n SyncMessenger,\n data:\n Uint8Array):\n Uint8Array {\n const\n { i32a,\n u8a,\n headerLength,\n maxDataLength } =\n messenger;\n const\n requestLength =\n data.byteLength;\n // check whether request is too large\n if ( requestLength > maxDataLength ) {\n throw new RangeError(\n `Request is too large: ` +\n `${ requestLength } > ${ maxDataLength }. ` +\n `Consider grow the size of SharedArrayBuffer.`);\n }\n // lock main thread\n Atomics.store(\n i32a,\n MAIN_LOCK_INDEX,\n MAIN_LOCKED);\n // payload and length\n i32a[\n DATA_INDEX] =\n requestLength;\n u8a.set(\n data,\n headerLength);\n // wakeup worker\n // Atomics.notify(i32a, WORKER_LOCK_INDEX);\n // this may not work\n Atomics.store(\n i32a,\n WORKER_LOCK_INDEX,\n WORKER_UNLOCKED);\n // wait for worker to finish\n sleepUntil(\n () => Atomics.load(\n i32a,\n MAIN_LOCK_INDEX) === MAIN_UNLOCKED);\n // worker return\n const\n responseLength =\n i32a[\n DATA_INDEX];\n const\n response =\n u8a.slice(\n headerLength,\n headerLength + responseLength);\n return response;\n}\n\n/**\n * Responds to main thread from worker thread.\n * Used in worker thread.\n * @param messenger - SyncMessenger\n * @param transfer - Function to transfer request data.\n */\nexport\n async function\n respondToMainFromWorker(\n messenger:\n SyncMessenger,\n transfer:\n (data:\n Uint8Array) => Promise<Uint8Array>):\n Promise<void> {\n const\n { i32a,\n u8a,\n headerLength,\n maxDataLength } =\n messenger;\n while (\n true) {\n if ( Atomics.load(\n i32a,\n WORKER_LOCK_INDEX) === WORKER_UNLOCKED) {\n break;\n }\n }\n // because of `Atomics.notify` may not work\n // const\n // waitRes =\n // Atomics.wait(\n // i32a,\n // WORKER_LOCK_INDEX,\n // WORKER_LOCKED);\n // if ( waitRes !== 'ok' ) {\n // throw new Error(\n // `Unexpected Atomics.wait ` +\n // `result: ${ waitRes }`);\n // }\n // payload and length\n const\n requestLength =\n i32a[\n DATA_INDEX];\n // console.log(\n // `requestLength: ${ requestLength }`);\n const\n data =\n u8a.slice(\n headerLength,\n headerLength + requestLength);\n // call async I/O operation\n let\n response =\n await transfer(\n data);\n const\n responseLength =\n response.byteLength;\n // check whether response is too large\n if ( responseLength > maxDataLength ) {\n const\n message =\n `Response is too large: ` +\n\t `${ responseLength } > ${ maxDataLength }. ` +\n\t `Consider grow the size of SharedArrayBuffer.`;\n response =\n encodeToBuffer(\n [ { name:\n 'RangeError',\n message } ]);\n // the error is too large?\n if ( response.byteLength > maxDataLength ) {\n // lock worker thread before throw\n Atomics.store(\n i32a,\n WORKER_LOCK_INDEX,\n WORKER_LOCKED);\n throw new RangeError(\n message);\n }\n }\n // write response data\n i32a[\n DATA_INDEX] =\n response.byteLength;\n u8a.set(\n response,\n headerLength);\n // lock worker thread\n Atomics.store(\n i32a,\n WORKER_LOCK_INDEX,\n WORKER_LOCKED);\n // wakeup main thread\n Atomics.store(\n i32a,\n MAIN_LOCK_INDEX,\n MAIN_UNLOCKED);\n}\n","// SPDX-License-Identifier: GPL-3.0-or-later\n\n/** ----------------------------------------------------------------------\n * Copyright ©\n * Jiang Jie\n * 2024, 2025\n * Pellegrino Prevete\n * 2025\n * \n * All rights reserved\n * ----------------------------------------------------------------------\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n * \n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\n */\n\nimport { Err,\n Ok,\n type IOResult,\n type VoidIOResult } from 'happy-rusty';\nimport invariant from 'tiny-invariant';\nimport type { CopyOptions,\n ExistsOptions,\n FileLike,\n FileSystemHandleLike,\n MoveOptions,\n ReadDirEntrySync,\n ReadDirOptions,\n ReadFileContent,\n ReadOptions,\n SyncAgentOptions,\n TempOptions,\n WriteOptions,\n WriteSyncFileContent,\n ZipOptions } from '../fs/defines.ts';\nimport { deserializeError,\n\t setGlobalOpTimeout } from './helpers.ts';\nimport { callWorkerFromMain,\n decodeFromBuffer,\n decodeToString,\n encodeToBuffer,\n SyncMessenger,\n WorkerAsyncOp } from './shared.ts';\n\n/**\n * Cache the messenger instance.\n */\nlet\n messenger:\n SyncMessenger;\n\n/**\n * Communicate with worker.\n * @param options - SyncAgentOptions\n * @returns\n */\nexport\n function\n connectSyncAgent(\n options:\n SyncAgentOptions):\n Promise<void> {\n if ( typeof window === 'undefined' ) {\n throw new Error(\n 'Only can use in main thread');\n }\n if ( messenger ) {\n throw new Error(\n 'Main messenger already started');\n }\n return new Promise(\n resolve => {\n const\n { worker,\n bufferLength = 1024 * 1024,\n opTimeout = 1000 } =\n options;\n // check parameters\n invariant(\n worker instanceof Worker ||\n worker instanceof URL ||\n ( typeof worker === 'string' &&\n worker),\n\t () => 'Worker must be Worker or valid URL(string).');\n invariant(\n bufferLength > 16 &&\n bufferLength % 4 === 0,\n () => 'bufferLength must be a multiple of 4.')\n invariant(\n Number.isInteger(\n opTimeout) &&\n opTimeout > 0,\n () => 'opTimeout must be integer and greater than 0.');\n setGlobalOpTimeout(\n opTimeout);\n const\n workerAdapter =\n worker instanceof Worker ?\n worker :\n new Worker(\n worker);\n const\n sab =\n new SharedArrayBuffer(\n bufferLength);\n workerAdapter.addEventListener(\n 'message',\n (event:\n MessageEvent<boolean>) => {\n if ( event.data ) {\n messenger =\n new SyncMessenger(\n sab);\n resolve();\n }\n });\n workerAdapter.postMessage(\n sab);\n });\n}\n\n/**\n * Get messenger instance.\n * Use `setSyncMessenger` to pass the messenger to other environments for sharing.\n *\n * @returns SyncMessenger instance.\n */\nexport\n function\n getSyncMessenger():\n SyncMessenger {\n return messenger;\n}\n\n/**\n * Set messenger instance.\n * Use this method to share messenger with other environments.\n *\n * @param syncMessenger - SyncMessenger instance.\n */\nexport\n function\n setSyncMessenger(\n syncMessenger:\n SyncMessenger):\n void {\n invariant(\n syncMessenger != null,\n () => 'syncMessenger is null or undefined.');\n messenger =\n syncMessenger;\n}\n\n/**\n * Call worker I/O operation.\n * @param op - I/O operation enum.\n * @param args - I/O operation arguments.\n * @returns - I/O operation result.\n */\n// The linter here shows not to work well as it doesnt\n// support ignoring blocks.\n// https://eslint.org/docs/latest/use/configure/rules\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction callWorkerOp<T>(op: WorkerAsyncOp, ...args: any[]): IOResult<T> {\n if ( !messenger ) {\n // too early\n return Err(\n new Error(\n 'Worker not initialized. Come back later.'));\n }\n const\n request =\n [ op,\n ...args ];\n const\n requestData =\n encodeToBuffer(\n request);\n try {\n const\n response =\n callWorkerFromMain(\n messenger,\n requestData);\n const\n decodedResponse =\n decodeFromBuffer(\n response) as [ Error, T ];\n const\n _error =\n decodedResponse[\n 0];\n const\n result:\n IOResult<T> =\n _error ?\n Err(\n deserializeError(\n _error)) :\n Ok(\n ( decodedResponse[\n 1] ?? undefined ) as T);\n return result;\n }\n catch (\n _error) {\n return Err(\n _error as Error);\n }\n}\n\n/**\n * Sync version of `createFile`.\n */\nexport\n function\n createFileSync(\n filePath:\n string):\n VoidIOResult {\n return callWorkerOp(\n WorkerAsyncOp.createFile,\n filePath);\n}\n\n/**\n * Sync version of `mkdir`.\n */\nexport\n function\n mkdirSync(\n dirPath:\n string):\n VoidIOResult {\n return callWorkerOp(\n WorkerAsyncOp.mkdir,\n dirPath);\n}\n\n/**\n * Sync version of `move`.\n */\nexport\n function\n moveSync(\n srcPath:\n string,\n destPath:\n string,\n options?:\n MoveOptions):\n VoidIOResult {\n return callWorkerOp(\n WorkerAsyncOp.move,\n srcPath,\n destPath,\n options);\n}\n\n/**\n * Sync version of `readDir`.\n */\nexport\n function\n readDirSync(\n dirPath:\n string,\n options?:\n ReadDirOptions):\n IOResult<ReadDirEntrySync[]> {\n return callWorkerOp(\n WorkerAsyncOp.readDir,\n dirPath,\n options);\n}\n\n/**\n * Sync version of `readFile`.\n */\nexport\n function\n readFileSync(\n filePath:\n string,\n options:\n ReadOptions &\n { encoding:\n 'blob'; }):\n IOResult<FileLike>;\nexport\n function\n readFileSync(\n filePath:\n string,\n options:\n ReadOptions &\n { encoding:\n 'utf8'; }):\n IOResult<string>;\nexport\n function\n readFileSync(\n filePath:\n string,\n options?:\n ReadOptions &\n { encoding:\n 'binary'; }):\n IOResult<ArrayBuffer>;\nexport\n function\n readFileSync<T extends ReadFileContent>(\n filePath:\n string,\n options?:\n ReadOptions):\n IOResult<T> {\n const\n res:\n IOResult<FileLike> =\n callWorkerOp(\n WorkerAsyncOp.readBlobFile,\n filePath);\n return res.map(\n file => {\n // actually data is number array\n const\n u8a =\n new Uint8Array(\n file.data);\n file.data =\n u8a.buffer.slice(\n u8a.byteOffset,\n u8a.byteOffset + u8a.byteLength);\n switch (\n options?.encoding) {\n case 'blob': {\n return file as unknown as T;\n }\n case 'utf8': {\n return decodeToString(new Uint8Array(file.data)) as unknown as T;\n }\n default: {\n return file.data as unknown as T;\n }\n }\n });\n}\n\n/**\n * Sync version of `remove`.\n */\nexport\n function\n removeSync(\n path:\n string):\n VoidIOResult {\n return callWorkerOp(\n WorkerAsyncOp.remove,\n path);\n}\n\n/**\n * Sync version of `stat`.\n */\nexport\n function\n statSync(\n path:\n string):\n IOResult<FileSystemHandleLike> {\n return callWorkerOp(\n WorkerAsyncOp.stat,\n path);\n}\n\n/**\n * Serialize contents to an byte array or a string\n * that can be sent to worker.\n * @param contents\n * @returns\n */\nfunction\n serializeWriteContents(\n contents:\n WriteSyncFileContent):\n number[] |\n string {\n return contents instanceof ArrayBuffer ?\n [ ...new Uint8Array(\n contents) ] :\n ArrayBuffer.isView(\n contents) ?\n [ ...new Uint8Array(\n contents.buffer) ] :\n contents;\n}\n\n/**\n * Sync version of `writeFile`.\n */\nexport\n function\n writeFileSync(\n filePath:\n string,\n contents:\n WriteSyncFileContent,\n options?:\n WriteOptions):\n VoidIOResult {\n return callWorkerOp(\n WorkerAsyncOp.writeFile,\n filePath,\n serializeWriteContents(\n contents),\n options);\n}\n\n/**\n * Sync version of `appendFile`.\n */\nexport\n function\n appendFileSync(\n filePath:\n string,\n contents:\n WriteSyncFileContent):\n VoidIOResult {\n return callWorkerOp(\n WorkerAsyncOp.appendFile,\n filePath,\n serializeWriteContents(\n contents));\n}\n\n/**\n * Sync version of `copy`.\n */\nexport\n function\n copySync(\n srcPath:\n string,\n destPath:\n string,\n options?:\n CopyOptions):\n VoidIOResult {\n return callWorkerOp(\n WorkerAsyncOp.copy,\n srcPath,\n destPath,\n options);\n}\n\n/**\n * Sync version of `emptyDir`.\n */\nexport\n function\n emptyDirSync(\n dirPath:\n string):\n VoidIOResult {\n return callWorkerOp(\n WorkerAsyncOp.emptyDir,\n dirPath);\n}\n\n/**\n * Sync version of `exists`.\n */\nexport\n function\n existsSync(\n path:\n string,\n options?:\n ExistsOptions):\n IOResult<boolean> {\n return callWorkerOp(\n WorkerAsyncOp.exists,\n path,\n options);\n}\n\n/**\n * Sync version of `deleteTemp`.\n */\nexport\n function\n deleteTempSync():\n VoidIOResult {\n return callWorkerOp(\n WorkerAsyncOp.deleteTemp);\n}\n\n/**\n * Sync version of `mkTemp`.\n */\nexport\n function\n mkTempSync(\n options?:\n TempOptions):\n IOResult<string> {\n return callWorkerOp(\n WorkerAsyncOp.mkTemp,\n options);\n}\n\n/**\n * Sync version of `pruneTemp`.\n */\nexport\n function\n pruneTempSync(\n expired:\n Date):\n VoidIOResult {\n return callWorkerOp(\n WorkerAsyncOp.pruneTemp,\n expired);\n}\n\n/**\n * Sync version of `readBlobFile`.\n */\nexport\n function\n readBlobFileSync(\n filePath:\n string):\n IOResult<FileLike> {\n return readFileSync(\n filePath, {\n encoding:\n 'blob' });\n}\n\n/**\n * Sync version of `readJsonFile`.\n */\nexport\n function\n readJsonFileSync<T>(\n filePath:\n string):\n IOResult<T> {\n return readTextFileSync(\n filePath).andThen(\n contents => {\n try {\n return Ok(\n JSON.parse(\n contents));\n }\n catch (\n _error) {\n return Err(\n _error as Error);\n }\n });\n}\n\n/**\n * Sync version of `readTextFile`.\n */\nexport\n function\n readTextFileSync(\n filePath:\n string):\n IOResult<string> {\n return readFileSync(\n filePath,\n { encoding:\n 'utf8' });\n}\n\n/**\n * Sync version of `unzip`.\n */\nexport\n function\n unzipSync(\n zipFilePath:\n string,\n targetPath:\n string):\n VoidIOResult {\n return callWorkerOp(\n WorkerAsyncOp.unzip,\n zipFilePath,\n targetPath);\n}\n\n/**\n * Sync version of `zip`.\n */\nexport\n function\n zipSync(\n sourcePath:\n string,\n zipFilePath:\n string,\n options?:\n ZipOptions):\n VoidIOResult;\n\n/**\n * Sync version of `zip`.\n */\nexport\n function\n zipSync(\n sourcePath:\n string,\n options?:\n ZipOptions):\n IOResult<Uint8Array>;\n\n/**\n * Sync version of `zip`.\n */\nexport\n function\n zipSync<T>(\n sourcePath:\n string,\n zipFilePath?:\n string |\n ZipOptions,\n options?:\n ZipOptions):\n IOResult<T> {\n const\n res =\n callWorkerOp(\n WorkerAsyncOp.zip,\n sourcePath,\n zipFilePath,\n options) as IOResult<number[]> |\n VoidIOResult;\n return res.map(\n data => {\n return ( data ?\n new Uint8Array(\n data) :\n data) as T;\n });\n}\n"],"names":[],"mappings":";;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;;ACbP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;;AC1CP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AClRA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;AC5EP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;ACxCP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;AC3EP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;;AC5CP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;ACxBP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;AChCP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;AC7CP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;AC3BP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACO;;AC3CP;AACA;AACA;AACA;AACA;AACO;;ACmCP;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;ACnDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACO;AACP;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;;;"}
package/docs/README.md ADDED
@@ -0,0 +1,115 @@
1
+ **@themartiancompany/opfs**
2
+
3
+ ***
4
+
5
+ # @themartiancompany/opfs
6
+
7
+ ## Classes
8
+
9
+ | Class | Description |
10
+ | ------ | ------ |
11
+ | [SyncMessenger](classes/SyncMessenger.md) | Inspired by [memfs](https://github.com/streamich/memfs/blob/master/src/fsa-to-node/worker/SyncMessenger.ts). |
12
+
13
+ ## Interfaces
14
+
15
+ | Interface | Description |
16
+ | ------ | ------ |
17
+ | [CopyOptions](interfaces/CopyOptions.md) | Options for `copy`. |
18
+ | [DownloadFileTempResponse](interfaces/DownloadFileTempResponse.md) | Result of `downloadFile` when the file is saved to a temporary path. |
19
+ | [ErrorLike](interfaces/ErrorLike.md) | Serializable version of Error. |
20
+ | [ExistsOptions](interfaces/ExistsOptions.md) | Options to determine the existence of a file or directory. |
21
+ | [FileLike](interfaces/FileLike.md) | Serializable version of File. |
22
+ | [FileSystemFileHandleLike](interfaces/FileSystemFileHandleLike.md) | A handle to a file or directory returned by `statSync`. |
23
+ | [FileSystemHandleLike](interfaces/FileSystemHandleLike.md) | A handle to a file or directory returned by `statSync`. |
24
+ | [MoveOptions](interfaces/MoveOptions.md) | Options for `move`. |
25
+ | [ReadDirEntry](interfaces/ReadDirEntry.md) | An entry returned by `readDir`. |
26
+ | [ReadDirEntrySync](interfaces/ReadDirEntrySync.md) | An entry returned by `readDirSync`. |
27
+ | [ReadDirOptions](interfaces/ReadDirOptions.md) | Options for reading directories. |
28
+ | [ReadOptions](interfaces/ReadOptions.md) | Options for reading files with specified encoding. |
29
+ | [SyncAgentOptions](interfaces/SyncAgentOptions.md) | Setup options of `connectSyncAgent`. |
30
+ | [TempOptions](interfaces/TempOptions.md) | Options for `mkTemp`. |
31
+ | [UploadRequestInit](interfaces/UploadRequestInit.md) | fetch-t request options for uploading files. |
32
+ | [WriteOptions](interfaces/WriteOptions.md) | Options for writing files, including flags for creation and appending. |
33
+ | [ZipOptions](interfaces/ZipOptions.md) | Options for `zip`. |
34
+
35
+ ## Type Aliases
36
+
37
+ | Type alias | Description |
38
+ | ------ | ------ |
39
+ | [FileEncoding](type-aliases/FileEncoding.md) | Supported file encodings for reading and writing files. |
40
+ | [FsRequestInit](type-aliases/FsRequestInit.md) | fetch-t options for download and upload. |
41
+ | [ReadFileContent](type-aliases/ReadFileContent.md) | Represents the possible content types that can be read from a file. |
42
+ | [WriteFileContent](type-aliases/WriteFileContent.md) | Represents the possible content types that can be written to a file. |
43
+ | [WriteSyncFileContent](type-aliases/WriteSyncFileContent.md) | Represents the possible content types that can be written synchronously to a file. |
44
+
45
+ ## Variables
46
+
47
+ | Variable | Description |
48
+ | ------ | ------ |
49
+ | [CURRENT\_DIR](variables/CURRENT_DIR.md) | A constant representing the current directory path. |
50
+ | [NOT\_FOUND\_ERROR](variables/NOT_FOUND_ERROR.md) | A constant representing the error thrown when a file or directory is not found. |
51
+ | [ROOT\_DIR](variables/ROOT_DIR.md) | A constant representing the root directory path. |
52
+ | [TMP\_DIR](variables/TMP_DIR.md) | A constant representing the temporary directory path. |
53
+
54
+ ## Functions
55
+
56
+ | Function | Description |
57
+ | ------ | ------ |
58
+ | [appendFile](functions/appendFile.md) | Appends content to a file at the specified path. |
59
+ | [appendFileSync](functions/appendFileSync.md) | Sync version of `appendFile`. |
60
+ | [assertAbsolutePath](functions/assertAbsolutePath.md) | Asserts that the provided path is an absolute path. |
61
+ | [assertFileUrl](functions/assertFileUrl.md) | Asserts that the provided URL is a valid file URL. |
62
+ | [connectSyncAgent](functions/connectSyncAgent.md) | Communicate with worker. |
63
+ | [copy](functions/copy.md) | Copies a file or directory from one location to another same as `cp -r`. |
64
+ | [copySync](functions/copySync.md) | Sync version of `copy`. |
65
+ | [createFile](functions/createFile.md) | Creates a new file at the specified path same as `touch`. |
66
+ | [createFileSync](functions/createFileSync.md) | Sync version of `createFile`. |
67
+ | [deleteTemp](functions/deleteTemp.md) | Delete the temporary directory and all its contents. |
68
+ | [deleteTempSync](functions/deleteTempSync.md) | Sync version of `deleteTemp`. |
69
+ | [downloadFile](functions/downloadFile.md) | Downloads a file from a URL and saves it to a temporary file. The returned response will contain the temporary file path. |
70
+ | [emptyDir](functions/emptyDir.md) | Empties the contents of a directory at the specified path. |
71
+ | [emptyDirSync](functions/emptyDirSync.md) | Sync version of `emptyDir`. |
72
+ | [exists](functions/exists.md) | Checks whether a file or directory exists at the specified path. |
73
+ | [existsSync](functions/existsSync.md) | Sync version of `exists`. |
74
+ | [generateTempPath](functions/generateTempPath.md) | Generate a temporary path but not create it. |
75
+ | [getFileDataByHandle](functions/getFileDataByHandle.md) | Gets the data of a file handle. |
76
+ | [getSyncMessenger](functions/getSyncMessenger.md) | Get messenger instance. Use `setSyncMessenger` to pass the messenger to other environments for sharing. |
77
+ | [isDirectoryHandle](functions/isDirectoryHandle.md) | Whether the handle is a directory. |
78
+ | [isFileHandle](functions/isFileHandle.md) | Whether the handle is a file. |
79
+ | [isFileHandleLike](functions/isFileHandleLike.md) | Whether the handle is a file-like. |
80
+ | [isOPFSSupported](functions/isOPFSSupported.md) | Checks if the Origin Private File System (OPFS) is supported in the current environment. |
81
+ | [isTempPath](functions/isTempPath.md) | Check whether the path is a temporary path. |
82
+ | [mkdir](functions/mkdir.md) | Creates a new directory at the specified path same as `mkdir -p`. |
83
+ | [mkdirSync](functions/mkdirSync.md) | Sync version of `mkdir`. |
84
+ | [mkTemp](functions/mkTemp.md) | Create a temporary file or directory. |
85
+ | [mkTempSync](functions/mkTempSync.md) | Sync version of `mkTemp`. |
86
+ | [move](functions/move.md) | Move a file or directory from an old path to a new path. |
87
+ | [moveSync](functions/moveSync.md) | Sync version of `move`. |
88
+ | [pruneTemp](functions/pruneTemp.md) | Prune the temporary directory and delete all expired files. |
89
+ | [pruneTempSync](functions/pruneTempSync.md) | Sync version of `pruneTemp`. |
90
+ | [readBlobFile](functions/readBlobFile.md) | Reads the content of a file at the specified path as a File. |
91
+ | [readBlobFileSync](functions/readBlobFileSync.md) | Sync version of `readBlobFile`. |
92
+ | [readDir](functions/readDir.md) | Reads the contents of a directory at the specified path. |
93
+ | [readDirSync](functions/readDirSync.md) | Sync version of `readDir`. |
94
+ | [readFile](functions/readFile.md) | Reads the content of a file at the specified path with the specified options. |
95
+ | [readFileSync](functions/readFileSync.md) | Sync version of `readFile`. |
96
+ | [readJsonFile](functions/readJsonFile.md) | Reads the content of a file at the specified path as a string and returns it as a JSON object. |
97
+ | [readJsonFileSync](functions/readJsonFileSync.md) | Sync version of `readJsonFile`. |
98
+ | [readTextFile](functions/readTextFile.md) | Reads the content of a file at the specified path as a string. |
99
+ | [readTextFileSync](functions/readTextFileSync.md) | Sync version of `readTextFile`. |
100
+ | [remove](functions/remove.md) | Removes a file or directory at the specified path same as `rm -rf`. |
101
+ | [removeSync](functions/removeSync.md) | Sync version of `remove`. |
102
+ | [setSyncMessenger](functions/setSyncMessenger.md) | Set messenger instance. Use this method to share messenger with other environments. |
103
+ | [startSyncAgent](functions/startSyncAgent.md) | Start worker agent. Listens to postMessage from main thread. Start runner loop. |
104
+ | [stat](functions/stat.md) | Retrieves the status of a file or directory at the specified path. |
105
+ | [statSync](functions/statSync.md) | Sync version of `stat`. |
106
+ | [toFileSystemHandleLike](functions/toFileSystemHandleLike.md) | Serialize a `FileSystemHandle` to plain object. |
107
+ | [unzip](functions/unzip.md) | Unzip a zip file to a directory. Equivalent to `unzip -o <zipFilePath> -d <targetPath> |
108
+ | [unzipFromUrl](functions/unzipFromUrl.md) | Unzip a remote zip file to a directory. Equivalent to `unzip -o <zipFilePath> -d <targetPath> |
109
+ | [unzipSync](functions/unzipSync.md) | Sync version of `unzip`. |
110
+ | [uploadFile](functions/uploadFile.md) | Uploads a file from the specified path to a URL. |
111
+ | [writeFile](functions/writeFile.md) | Writes content to a file at the specified path. |
112
+ | [writeFileSync](functions/writeFileSync.md) | Sync version of `writeFile`. |
113
+ | [zip](functions/zip.md) | Zip a file or directory and write to a zip file. Equivalent to `zip -r <zipFilePath> <targetPath>`. |
114
+ | [zipFromUrl](functions/zipFromUrl.md) | Zip a remote file and write to a zip file. |
115
+ | [zipSync](functions/zipSync.md) | Sync version of `zip`. |
@@ -0,0 +1,42 @@
1
+ [**@themartiancompany/opfs**](../README.md)
2
+
3
+ ***
4
+
5
+ [@themartiancompany/opfs](../README.md) / SyncMessenger
6
+
7
+ # Class: SyncMessenger
8
+
9
+ Defined in: worker/shared.ts:203
10
+
11
+ Inspired by [memfs](https://github.com/streamich/memfs/blob/master/src/fsa-to-node/worker/SyncMessenger.ts).
12
+
13
+ Used both in main thread and worker thread.
14
+
15
+ ## Constructors
16
+
17
+ ### new SyncMessenger()
18
+
19
+ ```ts
20
+ new SyncMessenger(sab): SyncMessenger
21
+ ```
22
+
23
+ Defined in: worker/shared.ts:216
24
+
25
+ #### Parameters
26
+
27
+ | Parameter | Type |
28
+ | ------ | ------ |
29
+ | `sab` | `SharedArrayBuffer` |
30
+
31
+ #### Returns
32
+
33
+ [`SyncMessenger`](SyncMessenger.md)
34
+
35
+ ## Properties
36
+
37
+ | Property | Modifier | Type | Defined in |
38
+ | ------ | ------ | ------ | ------ |
39
+ | <a id="headerlength"></a> `headerLength` | `readonly` | `number` | worker/shared.ts:211 |
40
+ | <a id="i32a"></a> `i32a` | `readonly` | `Int32Array` | worker/shared.ts:205 |
41
+ | <a id="maxdatalength"></a> `maxDataLength` | `readonly` | `number` | worker/shared.ts:214 |
42
+ | <a id="u8a"></a> `u8a` | `readonly` | `Uint8Array` | worker/shared.ts:208 |
@@ -0,0 +1,29 @@
1
+ [**@themartiancompany/opfs**](../README.md)
2
+
3
+ ***
4
+
5
+ [@themartiancompany/opfs](../README.md) / appendFile
6
+
7
+ # Function: appendFile()
8
+
9
+ ```ts
10
+ function appendFile(filePath, contents): AsyncVoidIOResult
11
+ ```
12
+
13
+ Defined in: fs/opfs\_ext.ts:252
14
+
15
+ Appends content to a file at the specified path.
16
+
17
+ ## Parameters
18
+
19
+ | Parameter | Type | Description |
20
+ | ------ | ------ | ------ |
21
+ | `filePath` | `string` | The path of the file to append to. |
22
+ | `contents` | [`WriteFileContent`](../type-aliases/WriteFileContent.md) | The content to append to the file. |
23
+
24
+ ## Returns
25
+
26
+ `AsyncVoidIOResult`
27
+
28
+ A promise that resolves to an `AsyncIOResult`
29
+ indicating whether the content was successfully appended.
@@ -0,0 +1,26 @@
1
+ [**@themartiancompany/opfs**](../README.md)
2
+
3
+ ***
4
+
5
+ [@themartiancompany/opfs](../README.md) / appendFileSync
6
+
7
+ # Function: appendFileSync()
8
+
9
+ ```ts
10
+ function appendFileSync(filePath, contents): VoidIOResult
11
+ ```
12
+
13
+ Defined in: worker/opfs\_worker\_adapter.ts:439
14
+
15
+ Sync version of `appendFile`.
16
+
17
+ ## Parameters
18
+
19
+ | Parameter | Type |
20
+ | ------ | ------ |
21
+ | `filePath` | `string` |
22
+ | `contents` | [`WriteSyncFileContent`](../type-aliases/WriteSyncFileContent.md) |
23
+
24
+ ## Returns
25
+
26
+ `VoidIOResult`
@@ -0,0 +1,29 @@
1
+ [**@themartiancompany/opfs**](../README.md)
2
+
3
+ ***
4
+
5
+ [@themartiancompany/opfs](../README.md) / assertAbsolutePath
6
+
7
+ # Function: assertAbsolutePath()
8
+
9
+ ```ts
10
+ function assertAbsolutePath(path): void
11
+ ```
12
+
13
+ Defined in: fs/assertions.ts:37
14
+
15
+ Asserts that the provided path is an absolute path.
16
+
17
+ ## Parameters
18
+
19
+ | Parameter | Type | Description |
20
+ | ------ | ------ | ------ |
21
+ | `path` | `string` | The file path to validate. |
22
+
23
+ ## Returns
24
+
25
+ `void`
26
+
27
+ ## Throws
28
+
29
+ Will throw an error if the path is not an absolute path.
@@ -0,0 +1,29 @@
1
+ [**@themartiancompany/opfs**](../README.md)
2
+
3
+ ***
4
+
5
+ [@themartiancompany/opfs](../README.md) / assertFileUrl
6
+
7
+ # Function: assertFileUrl()
8
+
9
+ ```ts
10
+ function assertFileUrl(fileUrl): void
11
+ ```
12
+
13
+ Defined in: fs/assertions.ts:56
14
+
15
+ Asserts that the provided URL is a valid file URL.
16
+
17
+ ## Parameters
18
+
19
+ | Parameter | Type | Description |
20
+ | ------ | ------ | ------ |
21
+ | `fileUrl` | `string` | The file URL to validate. |
22
+
23
+ ## Returns
24
+
25
+ `void`
26
+
27
+ ## Throws
28
+
29
+ Will throw an error if the URL is not a valid file URL.
@@ -0,0 +1,25 @@
1
+ [**@themartiancompany/opfs**](../README.md)
2
+
3
+ ***
4
+
5
+ [@themartiancompany/opfs](../README.md) / connectSyncAgent
6
+
7
+ # Function: connectSyncAgent()
8
+
9
+ ```ts
10
+ function connectSyncAgent(options): Promise<void>
11
+ ```
12
+
13
+ Defined in: worker/opfs\_worker\_adapter.ts:69
14
+
15
+ Communicate with worker.
16
+
17
+ ## Parameters
18
+
19
+ | Parameter | Type | Description |
20
+ | ------ | ------ | ------ |
21
+ | `options` | [`SyncAgentOptions`](../interfaces/SyncAgentOptions.md) | SyncAgentOptions |
22
+
23
+ ## Returns
24
+
25
+ `Promise`\<`void`\>
@@ -0,0 +1,35 @@
1
+ [**@themartiancompany/opfs**](../README.md)
2
+
3
+ ***
4
+
5
+ [@themartiancompany/opfs](../README.md) / copy
6
+
7
+ # Function: copy()
8
+
9
+ ```ts
10
+ function copy(
11
+ srcPath,
12
+ destPath,
13
+ options?): AsyncVoidIOResult
14
+ ```
15
+
16
+ Defined in: fs/opfs\_ext.ts:279
17
+
18
+ Copies a file or directory from one location to another same as `cp -r`.
19
+
20
+ Both `srcPath` and `destPath` must both be a file or directory.
21
+
22
+ ## Parameters
23
+
24
+ | Parameter | Type | Description |
25
+ | ------ | ------ | ------ |
26
+ | `srcPath` | `string` | The source file/directory path. |
27
+ | `destPath` | `string` | The destination file/directory path. |
28
+ | `options`? | [`CopyOptions`](../interfaces/CopyOptions.md) | The copy options. |
29
+
30
+ ## Returns
31
+
32
+ `AsyncVoidIOResult`
33
+
34
+ A promise that resolves to an `AsyncVoidIOResult`
35
+ indicating whether the file was successfully copied.
@@ -0,0 +1,30 @@
1
+ [**@themartiancompany/opfs**](../README.md)
2
+
3
+ ***
4
+
5
+ [@themartiancompany/opfs](../README.md) / copySync
6
+
7
+ # Function: copySync()
8
+
9
+ ```ts
10
+ function copySync(
11
+ srcPath,
12
+ destPath,
13
+ options?): VoidIOResult
14
+ ```
15
+
16
+ Defined in: worker/opfs\_worker\_adapter.ts:457
17
+
18
+ Sync version of `copy`.
19
+
20
+ ## Parameters
21
+
22
+ | Parameter | Type |
23
+ | ------ | ------ |
24
+ | `srcPath` | `string` |
25
+ | `destPath` | `string` |
26
+ | `options`? | [`CopyOptions`](../interfaces/CopyOptions.md) |
27
+
28
+ ## Returns
29
+
30
+ `VoidIOResult`