opfs-tools
Version:
EN: A simple, high-performance, and comprehensive file system API running in the browser, built on [OPFS](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system).
1 lines • 35 kB
Source Map (JSON)
{"version":3,"file":"opfs-tools.umd.cjs","sources":["../src/access-worker.ts","../src/common.ts","../src/directory.ts","../src/file.ts","../src/tmpfile.ts","../src/rollfile.ts"],"sourcesContent":["import { FileSystemSyncAccessHandle } from './common';\nimport OPFSWorker from './opfs-worker?worker&inline';\n\nexport type OPFSWorkerAccessHandle = {\n read: (offset: number, size: number) => Promise<ArrayBuffer>;\n write: FileSystemSyncAccessHandle['write'];\n close: FileSystemSyncAccessHandle['close'];\n truncate: FileSystemSyncAccessHandle['truncate'];\n getSize: FileSystemSyncAccessHandle['getSize'];\n flush: FileSystemSyncAccessHandle['flush'];\n};\n\nexport type OpenMode = 'read-only' | 'readwrite' | 'readwrite-unsafe';\n\nexport async function createOPFSAccess(\n fileId: number,\n filePath: string,\n mode: OpenMode\n): Promise<OPFSWorkerAccessHandle> {\n const postMsg = getWorkerMsger();\n await postMsg('register', { fileId, filePath, mode });\n return {\n read: async (offset, size) =>\n (await postMsg('read', {\n fileId,\n offset,\n size,\n })) as ArrayBuffer,\n write: async (data, opts) =>\n (await postMsg(\n 'write',\n {\n fileId,\n data,\n opts,\n },\n [ArrayBuffer.isView(data) ? data.buffer : data]\n )) as number,\n close: async () =>\n (await postMsg('close', {\n fileId,\n })) as void,\n truncate: async (newSize: number) =>\n (await postMsg('truncate', {\n fileId,\n newSize,\n })) as void,\n getSize: async () =>\n (await postMsg('getSize', {\n fileId,\n })) as number,\n flush: async () =>\n (await postMsg('flush', {\n fileId,\n })) as void,\n };\n}\n\nconst msgerCache: Array<Function> = [];\nlet nextMsgerIdx = 0;\nfunction getWorkerMsger() {\n // Create a maximum of three workers\n if (msgerCache.length < 3) {\n const msger = create();\n msgerCache.push(msger);\n return msger;\n } else {\n const msger = msgerCache[nextMsgerIdx];\n nextMsgerIdx = (nextMsgerIdx + 1) % msgerCache.length;\n return msger;\n }\n\n function create() {\n const worker = new OPFSWorker();\n\n let cbId = 0;\n let cbFns: Record<number, { resolve: Function; reject: Function }> = {};\n\n worker.onmessage = ({\n data,\n }: {\n data: {\n cbId: number;\n returnVal?: unknown;\n evtType: string;\n errMsg: string;\n };\n }) => {\n if (data.evtType === 'callback') {\n cbFns[data.cbId]?.resolve(data.returnVal);\n } else if (data.evtType === 'throwError') {\n cbFns[data.cbId]?.reject(Error(data.errMsg));\n }\n delete cbFns[data.cbId];\n };\n\n return async function postMsg(\n evtType: string,\n args: unknown,\n trans: Transferable[] = []\n ) {\n cbId += 1;\n\n const rsP = new Promise((resolve, reject) => {\n cbFns[cbId] = { resolve, reject };\n });\n worker.postMessage(\n {\n cbId,\n evtType,\n args,\n },\n trans\n );\n\n return rsP;\n };\n }\n}\n","export interface FileSystemSyncAccessHandle {\n read: (container: ArrayBuffer, opts: { at: number }) => Promise<number>;\n write: (\n data: ArrayBuffer | ArrayBufferView,\n opts?: { at: number }\n ) => Promise<number>;\n flush: () => Promise<void>;\n close: () => Promise<void>;\n truncate: (newSize: number) => Promise<void>;\n getSize: () => Promise<number>;\n}\n\nexport function parsePath(path: string) {\n if (path === '/') return { parent: null, name: '' };\n\n const pathArr = path.split('/').filter((s) => s.length > 0);\n if (pathArr.length === 0) throw Error('Invalid path');\n\n const name = pathArr[pathArr.length - 1];\n\n const parent = '/' + pathArr.slice(0, -1).join('/');\n\n return { name, parent };\n}\n\nexport async function getFSHandle<\n ISFile extends boolean,\n ISCreate extends boolean,\n T = ISFile extends true ? FileSystemFileHandle : FileSystemDirectoryHandle,\n RT = ISCreate extends true ? T : T | null\n>(\n path: string,\n opts: {\n create?: ISCreate;\n isFile?: ISFile;\n }\n): Promise<RT> {\n const { parent, name } = parsePath(path);\n\n if (parent == null) return (await navigator.storage.getDirectory()) as RT;\n\n const dirPaths = parent.split('/').filter((s) => s.length > 0);\n\n try {\n let dirHandle = await navigator.storage.getDirectory();\n for (const p of dirPaths) {\n dirHandle = await dirHandle.getDirectoryHandle(p, {\n create: opts.create,\n });\n }\n if (opts.isFile) {\n return (await dirHandle.getFileHandle(name, {\n create: opts.create,\n })) as RT;\n } else {\n return (await dirHandle.getDirectoryHandle(name, {\n create: opts.create,\n })) as RT;\n }\n } catch (err) {\n if ((err as Error).name === 'NotFoundError') {\n return null as RT;\n }\n throw err;\n }\n}\n\nexport async function remove(path: string) {\n const { parent, name } = parsePath(path);\n if (parent == null) {\n const root = await navigator.storage.getDirectory();\n for await (const it of root.keys()) {\n await root.removeEntry(it, { recursive: true });\n }\n return;\n }\n\n const dirHandle = (await getFSHandle(parent, {\n create: false,\n isFile: false,\n })) as FileSystemDirectoryHandle | null;\n if (dirHandle == null) return;\n\n try {\n await dirHandle.removeEntry(name, { recursive: true });\n } catch (err) {\n if ((err as Error).name === 'NotFoundError') return;\n throw err;\n }\n}\n\nexport function joinPath(p1: string, p2: string) {\n return `${p1}/${p2}`.replace('//', '/');\n}\n","import { getFSHandle, joinPath, parsePath, remove } from './common';\nimport { file, OTFile } from './file';\n\ndeclare global {\n interface FileSystemDirectoryHandle {\n keys: () => AsyncIterable<string>;\n values: () => AsyncIterable<\n FileSystemDirectoryHandle | FileSystemFileHandle\n >;\n }\n}\n\n/**\n * Represents a directory with utility functions.\n * @param {string} dirPath - The path of the directory.\n * @returns An object with directory utility functions.\n * \n * @example\n // Create a directory\n await dir('/path/to/directory').create();\n\n // Check if the directory exists\n const exists = await dir('/path/to/directory').exists();\n\n // Remove the directory\n\n // Retrieve children of the directory\n const children = await dir('/path/to/parent_directory').children();\n */\nexport function dir(dirPath: string) {\n return new OTDir(dirPath);\n}\n\nexport class OTDir {\n get kind(): 'dir' {\n return 'dir';\n }\n\n get name() {\n return this.#name;\n }\n\n get path() {\n return this.#path;\n }\n\n get parent(): OTDir | null {\n return this.#parentPath == null ? null : dir(this.#parentPath);\n }\n\n #path: string;\n #name: string;\n #parentPath: string | null;\n\n constructor(dirPath: string) {\n this.#path = dirPath;\n const { parent, name } = parsePath(dirPath);\n this.#name = name;\n this.#parentPath = parent;\n }\n\n /**\n * Creates the directory.\n * return A promise that resolves when the directory is created.\n */\n async create() {\n await getFSHandle(this.#path, {\n create: true,\n isFile: false,\n });\n return dir(this.#path);\n }\n\n /**\n * Checks if the directory exists.\n * return A promise that resolves to true if the directory exists, otherwise false.\n */\n async exists() {\n return (\n (await getFSHandle(this.#path, {\n create: false,\n isFile: false,\n })) instanceof FileSystemDirectoryHandle\n );\n }\n\n /**\n * Removes the directory.\n * return A promise that resolves when the directory is removed.\n */\n async remove() {\n for (const it of await this.children()) {\n try {\n await it.remove();\n } catch (err) {\n console.warn(err);\n }\n }\n try {\n await remove(this.#path);\n } catch (err) {\n console.warn(err);\n }\n }\n\n /**\n * Retrieves the children of the directory.\n * return A promise that resolves to an array of objects representing the children.\n */\n async children(): Promise<Array<OTDir | OTFile>> {\n const handle = (await getFSHandle(this.#path, {\n create: false,\n isFile: false,\n })) as FileSystemDirectoryHandle;\n if (handle == null) return [];\n\n const rs = [];\n for await (const it of handle.values()) {\n rs.push((it.kind === 'file' ? file : dir)(joinPath(this.#path, it.name)));\n }\n return rs;\n }\n\n /**\n * If the dest folder exists, copy the current directory into the dest folder;\n * if the dest folder does not exist, rename the current directory to dest name.\n */\n async copyTo(dest: OTDir): Promise<OTDir>;\n async copyTo(dest: FileSystemDirectoryHandle): Promise<null>;\n async copyTo<T>(dest: T) {\n if (!(await this.exists())) {\n throw Error(`dir ${this.path} not exists`);\n }\n\n if (dest instanceof OTDir) {\n const newDir = (await dest.exists())\n ? dir(joinPath(dest.path, this.name))\n : dest;\n await newDir.create();\n await Promise.all((await this.children()).map((it) => it.copyTo(newDir)));\n return newDir;\n } else if (dest instanceof FileSystemDirectoryHandle) {\n await Promise.all(\n (\n await this.children()\n ).map(async (it) => {\n if (it.kind === 'file') {\n await it.copyTo(\n await dest.getFileHandle(it.name, { create: true })\n );\n } else {\n await it.copyTo(\n await dest.getDirectoryHandle(it.name, { create: true })\n );\n }\n })\n );\n return null;\n }\n throw Error('Illegal target type');\n }\n\n /**\n * move directory, copy then remove current\n */\n async moveTo(dest: OTDir): Promise<OTDir> {\n const newDir = await this.copyTo(dest);\n await this.remove();\n return newDir;\n }\n}\n","import {\n OPFSWorkerAccessHandle,\n OpenMode,\n createOPFSAccess,\n} from './access-worker';\nimport { getFSHandle, joinPath, parsePath, remove } from './common';\nimport { OTDir, dir } from './directory';\n\nconst fileCache = new Map<string, OTFile>();\n/**\n * Retrieves a file wrapper instance for the specified file path.\n * @param {string} filePath - The path of the file.\n * @param {'r' | 'rw' | 'rw-unsafe'} mode - A string specifying the locking mode for the access handle. The default value is \"rw\"\n * return A OTFile instance.\n * \n * @see [MDN createSyncAccessHandle](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle/createSyncAccessHandle)\n * \n * @example\n * // Read content from a file\n const fileContent = await file('/path/to/file.txt', 'r').text();\n console.log('File content:', fileContent);\n\n // Check if a file exists\n const fileExists = await file('/path/to/file.txt').exists();\n console.log('File exists:', fileExists);\n\n // Remove a file\n await file('/path/to/file.txt').remove();\n */\nexport function file(filePath: string, mode: ShortOpenMode = 'rw') {\n if (mode === 'rw') {\n const f = fileCache.get(filePath) ?? new OTFile(filePath, mode);\n fileCache.set(filePath, f);\n return f;\n }\n return new OTFile(filePath, mode);\n}\n\n/**\n * Writes content to the specified file.\n * @param {string} target - The path of the file.\n * @param {string | BufferSource | ReadableStream<BufferSource>} content - The content to write to the file.\n * return A promise that resolves when the content is written to the file.\n * \n * @example\n * // Write content to a file\n await write('/path/to/file.txt', 'Hello, world!');\n */\nexport async function write(\n target: string | OTFile,\n content: string | BufferSource | ReadableStream<BufferSource> | OTFile,\n opts = { overwrite: true }\n) {\n if (content instanceof OTFile) {\n await write(target, await content.stream(), opts);\n return;\n }\n\n const writer = await (target instanceof OTFile\n ? target\n : file(target, 'rw')\n ).createWriter();\n try {\n if (opts.overwrite) await writer.truncate(0);\n if (content instanceof ReadableStream) {\n const reader = content.getReader();\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n await writer.write(value);\n }\n } else {\n await writer.write(content);\n }\n } catch (err) {\n throw err;\n } finally {\n await writer.close();\n }\n}\n\nlet FILE_ID = 0;\nconst genFileId = () => ++FILE_ID;\n\ntype ShortOpenMode = 'r' | 'rw' | 'rw-unsafe';\n\n/**\n * Represents a wrapper for interacting with a file in the filesystem.\n */\nexport class OTFile {\n get kind(): 'file' {\n return 'file';\n }\n\n get path() {\n return this.#path;\n }\n\n get name() {\n return this.#name;\n }\n\n get parent(): ReturnType<typeof dir> | null {\n return this.#parentPath == null ? null : dir(this.#parentPath);\n }\n\n #path: string;\n #parentPath: string;\n #name: string;\n #mode: OpenMode;\n\n #id: number;\n constructor(filePath: string, mode: ShortOpenMode) {\n this.#id = genFileId();\n this.#path = filePath;\n this.#mode = (\n {\n r: 'read-only',\n rw: 'readwrite',\n 'rw-unsafe': 'readwrite-unsafe',\n } as const\n )[mode];\n const { parent, name } = parsePath(filePath);\n if (parent == null) throw Error('Invalid path');\n this.#name = name;\n this.#parentPath = parent;\n }\n\n #referCnt = 0;\n #getAccessHandle = (() => {\n let accPromise: Promise<\n [OPFSWorkerAccessHandle, () => Promise<void>]\n > | null = null;\n\n return () => {\n this.#referCnt += 1;\n if (accPromise != null) return accPromise;\n\n return (accPromise = new Promise(async (resolve, reject) => {\n try {\n const accHandle = await createOPFSAccess(\n this.#id,\n this.#path,\n this.#mode\n );\n resolve([\n accHandle,\n async () => {\n this.#referCnt -= 1;\n if (this.#referCnt > 0) return;\n\n accPromise = null;\n await accHandle.close();\n },\n ]);\n } catch (err) {\n reject(err);\n }\n }));\n };\n })();\n\n #writing = false;\n /**\n * Random write to file\n */\n async createWriter() {\n if (this.#mode === 'read-only') throw Error('file is read-only');\n if (this.#writing) throw Error('Other writer have not been closed');\n this.#writing = true;\n\n const txtEC = new TextEncoder();\n\n // append content by default\n const [accHandle, unref] = await this.#getAccessHandle();\n let pos = await accHandle.getSize();\n let closed = false;\n return {\n write: async (\n chunk: string | BufferSource,\n opts: { at?: number } = {}\n ) => {\n if (closed) throw Error('Writer is closed');\n const content = typeof chunk === 'string' ? txtEC.encode(chunk) : chunk;\n const at = opts.at ?? pos;\n const contentSize = content.byteLength;\n pos = at + contentSize;\n return await accHandle.write(content, { at });\n },\n truncate: async (size: number) => {\n if (closed) throw Error('Writer is closed');\n await accHandle.truncate(size);\n if (pos > size) pos = size;\n },\n flush: async () => {\n if (closed) throw Error('Writer is closed');\n await accHandle.flush();\n },\n close: async () => {\n if (closed) throw Error('Writer is closed');\n closed = true;\n this.#writing = false;\n await unref();\n },\n };\n }\n\n /**\n * Random access to file\n */\n async createReader() {\n const [accHandle, unref] = await this.#getAccessHandle();\n\n let closed = false;\n let pos = 0;\n return {\n read: async (size: number, opts: { at?: number } = {}) => {\n if (closed) throw Error('Reader is closed');\n const offset = opts.at ?? pos;\n const buf = await accHandle.read(offset, size);\n pos = offset + buf.byteLength;\n return buf;\n },\n getSize: async () => {\n if (closed) throw Error('Reader is closed');\n return await accHandle.getSize();\n },\n close: async () => {\n if (closed) return;\n closed = true;\n await unref();\n },\n };\n }\n\n async text() {\n return new TextDecoder().decode(await this.arrayBuffer());\n }\n\n async arrayBuffer() {\n const fh = await getFSHandle(this.#path, { create: false, isFile: true });\n if (fh == null) return new ArrayBuffer(0);\n return (await fh.getFile()).arrayBuffer();\n }\n\n async stream() {\n const ofile = await this.getOriginFile();\n if (ofile == null) {\n return new ReadableStream<Uint8Array>({\n pull: (ctrl) => {\n ctrl.close();\n },\n });\n }\n\n return ofile.stream();\n }\n\n async getOriginFile() {\n return (\n await getFSHandle(this.#path, { create: false, isFile: true })\n )?.getFile();\n }\n\n async getSize() {\n const fh = await getFSHandle(this.#path, { create: false, isFile: true });\n if (fh == null) return 0;\n return (await fh.getFile()).size;\n }\n\n async exists() {\n return (\n (await getFSHandle(this.#path, {\n create: false,\n isFile: true,\n })) instanceof FileSystemFileHandle\n );\n }\n\n async remove() {\n if (this.#referCnt) throw Error('exists unclosed reader/writer');\n await remove(this.#path);\n // fileCache.delete(this.#path);\n }\n\n /**\n * If the target is a file, use current overwrite the target;\n * if the target is a folder, copy the current file into that folder.\n */\n async copyTo(target: OTDir | OTFile): Promise<OTFile>;\n async copyTo(target: FileSystemFileHandle): Promise<null>;\n async copyTo<T>(target: T) {\n if (target instanceof OTFile) {\n if (target.path === this.path) return this;\n\n await write(target.path, this);\n return file(target.path);\n } else if (target instanceof OTDir) {\n if (!(await this.exists())) {\n throw Error(`file ${this.path} not exists`);\n }\n return await this.copyTo(file(joinPath(target.path, this.name)));\n } else if (target instanceof FileSystemFileHandle) {\n await (await this.stream()).pipeTo(await target.createWritable());\n return null;\n }\n throw Error('Illegal target type');\n }\n\n /**\n * move file, copy then remove current\n */\n async moveTo(target: OTDir | OTFile): Promise<OTFile> {\n const newFile = await this.copyTo(target);\n await this.remove();\n return newFile;\n }\n}\n","import { OTDir, dir } from './directory';\nimport { OTFile, file } from './file';\n\nconst TMP_DIR = '/.opfs-tools-temp-dir';\n\nasync function safeRemove(it: OTFile | OTDir) {\n try {\n if (it.kind === 'file') {\n if (!(await it.exists())) return true;\n\n const writer = await it.createWriter();\n await writer.truncate(0);\n await writer.close();\n await it.remove();\n } else {\n await it.remove();\n }\n return true;\n } catch (e) {\n console.warn(e);\n return false;\n }\n}\n\n// 'export' is for ease of testing\nexport function delByInterval() {\n setInterval(async () => {\n const timeOf3Days = 1000 * 60 * 60 * 24 * 3;\n for (const it of await dir(TMP_DIR).children()) {\n const match = /^\\d+-(\\d+)$/.exec(it.name);\n if (match == null || Date.now() - Number(match[1]) > timeOf3Days) {\n // Delete files that are older than three days and are not in writing\n await safeRemove(it);\n }\n }\n }, 60 * 1000);\n}\n\nconst currentPageTMPFiles: string[] = [];\nlet bindedUnloadEvt = false;\n\n// 'export' is for ease of testing\nexport async function delMarkFiles() {\n if (globalThis.localStorage == null) return;\n\n const opfsToolsExpires = 'OPFS_TOOLS_EXPIRES_TMP_FILES';\n\n if (!bindedUnloadEvt) {\n bindedUnloadEvt = true;\n globalThis.addEventListener('unload', () => {\n if (currentPageTMPFiles.length === 0) return;\n localStorage.setItem(\n opfsToolsExpires,\n `${\n localStorage.getItem(opfsToolsExpires) ?? ''\n },${currentPageTMPFiles.join(',')}`\n );\n });\n }\n\n let markStr = localStorage.getItem(opfsToolsExpires) ?? '';\n for (const name of markStr.split(',')) {\n if (name.length === 0) continue;\n if (await safeRemove(file(`${TMP_DIR}/${name}`))) {\n markStr = markStr.replace(name, '');\n }\n }\n localStorage.setItem(opfsToolsExpires, markStr.replace(/,{2,}/g, ','));\n}\n\n(async function init() {\n if (globalThis.__opfs_tools_tmpfile_init__ === true) return;\n globalThis.__opfs_tools_tmpfile_init__ = true;\n\n // not web context\n if (\n globalThis.FileSystemDirectoryHandle == null ||\n globalThis.FileSystemFileHandle == null ||\n globalThis.navigator?.storage.getDirectory == null\n ) {\n return;\n }\n\n // clear tmpfile\n delByInterval();\n await delMarkFiles();\n})();\n\n/**\n * Create a temporary file that will automatically be cleared to avoid occupying too much storage space.\n * The temporary file name will be automatically generated and stored in a specific directory.\n */\nexport function tmpfile() {\n const name = `${Math.random().toString().slice(2)}-${Date.now()}`;\n currentPageTMPFiles.push(name);\n return file(`${TMP_DIR}/${name}`);\n}\n","import { file } from './file';\n\nexport function rollfile(filePath: string, maxSize: number) {\n let f = file(filePath);\n\n let size = 0;\n let writerPromise: ReturnType<typeof f.createWriter> = f.createWriter();\n let readerPromise = f.createReader();\n\n const reset = async (writer: Awaited<typeof writerPromise>) => {\n const reader = await readerPromise;\n const data = await reader.read(size, { at: Math.round(size * 0.3) });\n size = await writer.write(data, { at: 0 });\n await writer.truncate(size);\n };\n\n return {\n append: async (content: string) => {\n const writer = await writerPromise;\n size += await writer.write(content);\n if (size >= maxSize) await reset(writer);\n },\n text: f.text.bind(f),\n remove: async () => {\n await (await readerPromise).close();\n await (await writerPromise).close();\n await f.remove();\n },\n getSize: async () => size,\n };\n}\n"],"names":["createOPFSAccess","fileId","filePath","mode","postMsg","getWorkerMsger","offset","size","data","opts","newSize","msgerCache","nextMsgerIdx","msger","create","worker","OPFSWorker","cbId","cbFns","_a","_b","evtType","args","trans","rsP","resolve","reject","parsePath","path","pathArr","s","name","parent","getFSHandle","dirPaths","dirHandle","p","err","remove","root","it","joinPath","p1","p2","dir","dirPath","OTDir","_OTDir","__privateAdd","_path","_name","_parentPath","__privateSet","__privateGet","handle","rs","file","dest","newDir","fileCache","f","OTFile","write","target","content","writer","reader","done","value","FILE_ID","genFileId","_OTFile","_mode","_id","_referCnt","_getAccessHandle","accPromise","accHandle","_writing","txtEC","unref","pos","closed","chunk","at","contentSize","buf","fh","ofile","ctrl","newFile","TMP_DIR","safeRemove","delByInterval","match","currentPageTMPFiles","bindedUnloadEvt","delMarkFiles","opfsToolsExpires","markStr","tmpfile","rollfile","maxSize","writerPromise","readerPromise","reset"],"mappings":"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"}