UNPKG

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).

479 lines (478 loc) 15.2 kB
var H = (r) => { throw TypeError(r); }; var L = (r, e, t) => e.has(r) || H("Cannot " + t); var s = (r, e, t) => (L(r, e, "read from private field"), t ? t.call(r) : e.get(r)), f = (r, e, t) => e.has(r) ? H("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(r) : e.set(r, t), u = (r, e, t, a) => (L(r, e, "write to private field"), a ? a.call(r, t) : e.set(r, t), t); const K = "KGZ1bmN0aW9uKCl7InVzZSBzdHJpY3QiO2Z1bmN0aW9uIHUocil7aWYocj09PSIvIilyZXR1cm57cGFyZW50Om51bGwsbmFtZToiIn07Y29uc3QgZT1yLnNwbGl0KCIvIikuZmlsdGVyKGk9PmkubGVuZ3RoPjApO2lmKGUubGVuZ3RoPT09MCl0aHJvdyBFcnJvcigiSW52YWxpZCBwYXRoIik7Y29uc3Qgbj1lW2UubGVuZ3RoLTFdLGE9Ii8iK2Uuc2xpY2UoMCwtMSkuam9pbigiLyIpO3JldHVybntuYW1lOm4scGFyZW50OmF9fWFzeW5jIGZ1bmN0aW9uIGgocixlKXtjb25zdHtwYXJlbnQ6bixuYW1lOmF9PXUocik7aWYobj09bnVsbClyZXR1cm4gYXdhaXQgbmF2aWdhdG9yLnN0b3JhZ2UuZ2V0RGlyZWN0b3J5KCk7Y29uc3QgaT1uLnNwbGl0KCIvIikuZmlsdGVyKHQ9PnQubGVuZ3RoPjApO3RyeXtsZXQgdD1hd2FpdCBuYXZpZ2F0b3Iuc3RvcmFnZS5nZXREaXJlY3RvcnkoKTtmb3IoY29uc3QgcyBvZiBpKXQ9YXdhaXQgdC5nZXREaXJlY3RvcnlIYW5kbGUocyx7Y3JlYXRlOmUuY3JlYXRlfSk7aWYoZS5pc0ZpbGUpcmV0dXJuIGF3YWl0IHQuZ2V0RmlsZUhhbmRsZShhLHtjcmVhdGU6ZS5jcmVhdGV9KX1jYXRjaCh0KXtpZih0Lm5hbWU9PT0iTm90Rm91bmRFcnJvciIpcmV0dXJuIG51bGw7dGhyb3cgdH19Y29uc3QgZj17fTtzZWxmLm9ubWVzc2FnZT1hc3luYyByPT57dmFyIGk7Y29uc3R7ZXZ0VHlwZTplLGFyZ3M6bn09ci5kYXRhO2xldCBhPWZbbi5maWxlUGF0aF07dHJ5e2xldCB0O2NvbnN0IHM9W107aWYoZT09PSJyZWdpc3RlciIpe2NvbnN0IGw9YXdhaXQgaChuLmZpbGVQYXRoLHtjcmVhdGU6ITAsaXNGaWxlOiEwfSk7aWYobD09bnVsbCl0aHJvdyBFcnJvcihgbm90IGZvdW5kIGZpbGU6ICR7bi5maWxlUGF0aH1gKTthPWF3YWl0IGwuY3JlYXRlU3luY0FjY2Vzc0hhbmRsZSgpLGZbbi5maWxlUGF0aF09YX1lbHNlIGlmKGU9PT0iY2xvc2UiKWF3YWl0IGEuY2xvc2UoKSxkZWxldGUgZltuLmZpbGVQYXRoXTtlbHNlIGlmKGU9PT0idHJ1bmNhdGUiKWF3YWl0IGEudHJ1bmNhdGUobi5uZXdTaXplKTtlbHNlIGlmKGU9PT0id3JpdGUiKXtjb25zdHtkYXRhOmwsb3B0czpvfT1yLmRhdGEuYXJnczt0PWF3YWl0IGEud3JpdGUobCxvKX1lbHNlIGlmKGU9PT0icmVhZCIpe2NvbnN0e29mZnNldDpsLHNpemU6b309ci5kYXRhLmFyZ3MsZz1uZXcgVWludDhBcnJheShvKSxkPWF3YWl0IGEucmVhZChnLHthdDpsfSksYz1nLmJ1ZmZlcjt0PWQ9PT1vP2M6KChpPWMudHJhbnNmZXIpPT1udWxsP3ZvaWQgMDppLmNhbGwoYyxkKSk/P2Muc2xpY2UoMCxkKSxzLnB1c2godCl9ZWxzZSBlPT09ImdldFNpemUiP3Q9YXdhaXQgYS5nZXRTaXplKCk6ZT09PSJmbHVzaCImJmF3YWl0IGEuZmx1c2goKTtzZWxmLnBvc3RNZXNzYWdlKHtldnRUeXBlOiJjYWxsYmFjayIsY2JJZDpyLmRhdGEuY2JJZCxyZXR1cm5WYWw6dH0scyl9Y2F0Y2godCl7Y29uc3Qgcz10O3NlbGYucG9zdE1lc3NhZ2Uoe2V2dFR5cGU6InRocm93RXJyb3IiLGNiSWQ6ci5kYXRhLmNiSWQsZXJyTXNnOnMubmFtZSsiOiAiK3MubWVzc2FnZStgCmArSlNPTi5zdHJpbmdpZnkoci5kYXRhKX0pfX19KSgpOwovLyMgc291cmNlTWFwcGluZ1VSTD1vcGZzLXdvcmtlci1CUWh5WXcwVy5qcy5tYXAK", O = (r) => Uint8Array.from(atob(r), (e) => e.charCodeAt(0)), E = typeof self < "u" && self.Blob && new Blob([O(K)], { type: "text/javascript;charset=utf-8" }); function j(r) { let e; try { if (e = E && (self.URL || self.webkitURL).createObjectURL(E), !e) throw ""; const t = new Worker(e, { name: r == null ? void 0 : r.name }); return t.addEventListener("error", () => { (self.URL || self.webkitURL).revokeObjectURL(e); }), t; } catch { return new Worker( "data:text/javascript;base64," + K, { name: r == null ? void 0 : r.name } ); } finally { e && (self.URL || self.webkitURL).revokeObjectURL(e); } } async function M(r) { const e = B(); return await e("register", { filePath: r }), { read: async (t, a) => await e("read", { filePath: r, offset: t, size: a }), write: async (t, a) => await e( "write", { filePath: r, data: t, opts: a }, [ArrayBuffer.isView(t) ? t.buffer : t] ), close: async () => await e("close", { filePath: r }), truncate: async (t) => await e("truncate", { filePath: r, newSize: t }), getSize: async () => await e("getSize", { filePath: r }), flush: async () => await e("flush", { filePath: r }) }; } const I = []; let T = 0; function B() { if (I.length < 3) { const e = r(); return I.push(e), e; } else { const e = I[T]; return T = (T + 1) % I.length, e; } function r() { const e = new j(); let t = 0, a = {}; return e.onmessage = ({ data: i }) => { var n, c; i.evtType === "callback" ? (n = a[i.cbId]) == null || n.resolve(i.returnVal) : i.evtType === "throwError" && ((c = a[i.cbId]) == null || c.reject(Error(i.errMsg))), delete a[i.cbId]; }, async function(n, c, l = []) { t += 1; const o = new Promise((b, X) => { a[t] = { resolve: b, reject: X }; }); return e.postMessage( { cbId: t, evtType: n, args: c }, l ), o; }; } } function R(r) { if (r === "/") return { parent: null, name: "" }; const e = r.split("/").filter((i) => i.length > 0); if (e.length === 0) throw Error("Invalid path"); const t = e[e.length - 1], a = "/" + e.slice(0, -1).join("/"); return { name: t, parent: a }; } async function d(r, e) { const { parent: t, name: a } = R(r); if (t == null) return await navigator.storage.getDirectory(); const i = t.split("/").filter((n) => n.length > 0); try { let n = await navigator.storage.getDirectory(); for (const c of i) n = await n.getDirectoryHandle(c, { create: e.create }); return e.isFile ? await n.getFileHandle(a, { create: e.create }) : await n.getDirectoryHandle(a, { create: e.create }); } catch (n) { if (n.name === "NotFoundError") return null; throw n; } } async function N(r) { const { parent: e, name: t } = R(r); if (e == null) { const i = await navigator.storage.getDirectory(); for await (const n of i.keys()) await i.removeEntry(n, { recursive: !0 }); return; } const a = await d(e, { create: !1, isFile: !1 }); a != null && await a.removeEntry(t, { recursive: !0 }); } function S(r, e) { return `${r}/${e}`.replace("//", "/"); } function g(r) { return new C(r); } var h, Y, p; class C { constructor(e) { f(this, h); f(this, Y); f(this, p); u(this, h, e); const { parent: t, name: a } = R(e); u(this, Y, a), u(this, p, t); } get kind() { return "dir"; } get name() { return s(this, Y); } get path() { return s(this, h); } get parent() { return s(this, p) == null ? null : g(s(this, p)); } /** * Creates the directory. * return A promise that resolves when the directory is created. */ async create() { return await d(s(this, h), { create: !0, isFile: !1 }), g(s(this, h)); } /** * Checks if the directory exists. * return A promise that resolves to true if the directory exists, otherwise false. */ async exists() { return await d(s(this, h), { create: !1, isFile: !1 }) instanceof FileSystemDirectoryHandle; } /** * Removes the directory. * return A promise that resolves when the directory is removed. */ async remove() { for (const e of await this.children()) try { await e.remove(); } catch (t) { console.warn(t); } try { await N(s(this, h)); } catch (e) { console.warn(e); } } /** * Retrieves the children of the directory. * return A promise that resolves to an array of objects representing the children. */ async children() { const e = await d(s(this, h), { create: !1, isFile: !1 }); if (e == null) return []; const t = []; for await (const a of e.values()) t.push((a.kind === "file" ? m : g)(S(s(this, h), a.name))); return t; } /** * If the dest folder exists, copy the current directory into the dest folder; * if the dest folder does not exist, rename the current directory to dest name. */ async copyTo(e) { if (!await this.exists()) throw Error(`dir ${this.path} not exists`); const t = await e.exists() ? g(S(e.path, this.name)) : e; return await t.create(), await Promise.all((await this.children()).map((a) => a.copyTo(t))), t; } /** * move directory, copy then remove current */ async moveTo(e) { const t = await this.copyTo(e); return await this.remove(), t; } } h = new WeakMap(), Y = new WeakMap(), p = new WeakMap(); const J = /* @__PURE__ */ new Map(); function m(r) { const e = J.get(r) ?? new G(r); return J.set(r, e), e; } async function V(r, e, t = { overwrite: !0 }) { if (e instanceof G) { await V(r, await e.stream(), t); return; } const a = await (r instanceof G ? r : m(r)).createWriter(); try { if (t.overwrite && await a.truncate(0), e instanceof ReadableStream) { const i = e.getReader(); for (; ; ) { const { done: n, value: c } = await i.read(); if (n) break; await a.write(c); } } else await a.write(e); } catch (i) { throw i; } finally { await a.close(); } } var w, Z, F, y, v, W; const k = class k { constructor(e) { f(this, w); f(this, Z); f(this, F); f(this, y, 0); f(this, v, /* @__PURE__ */ (() => { let e = null; return () => (u(this, y, s(this, y) + 1), e ?? (e = new Promise(async (t, a) => { try { const i = await M(s(this, w)); t([ i, async () => { u(this, y, s(this, y) - 1), !(s(this, y) > 0) && (e = null, await i.close()); } ]); } catch (i) { a(i); } }))); })()); f(this, W, !1); u(this, w, e); const { parent: t, name: a } = R(e); u(this, F, a), u(this, Z, t); } get kind() { return "file"; } get path() { return s(this, w); } get name() { return s(this, F); } get parent() { return s(this, Z) == null ? null : g(s(this, Z)); } /** * Random write to file */ async createWriter() { if (s(this, W)) throw Error("Other writer have not been closed"); u(this, W, !0); const e = new TextEncoder(), [t, a] = await s(this, v).call(this); let i = await t.getSize(), n = !1; return { write: async (c, l = {}) => { if (n) throw Error("Writer is closed"); const o = typeof c == "string" ? e.encode(c) : c, b = l.at ?? i, X = o.byteLength; return i = b + X, await t.write(o, { at: b }); }, truncate: async (c) => { if (n) throw Error("Writer is closed"); await t.truncate(c), i > c && (i = c); }, flush: async () => { if (n) throw Error("Writer is closed"); await t.flush(); }, close: async () => { if (n) throw Error("Writer is closed"); n = !0, u(this, W, !1), await a(); } }; } /** * Random access to file */ async createReader() { const [e, t] = await s(this, v).call(this); let a = !1, i = 0; return { read: async (n, c = {}) => { if (a) throw Error("Reader is closed"); const l = c.at ?? i, o = await e.read(l, n); return i = l + o.byteLength, o; }, getSize: async () => { if (a) throw Error("Reader is closed"); return await e.getSize(); }, close: async () => { a || (a = !0, await t()); } }; } async text() { return new TextDecoder().decode(await this.arrayBuffer()); } async arrayBuffer() { const e = await d(s(this, w), { create: !1, isFile: !0 }); return e == null ? new ArrayBuffer(0) : (await e.getFile()).arrayBuffer(); } async stream() { const e = await this.getOriginFile(); return e == null ? new ReadableStream({ pull: (t) => { t.close(); } }) : e.stream(); } async getOriginFile() { var e; return (e = await d(s(this, w), { create: !1, isFile: !0 })) == null ? void 0 : e.getFile(); } async getSize() { const e = await d(s(this, w), { create: !1, isFile: !0 }); return e == null ? 0 : (await e.getFile()).size; } async exists() { return await d(s(this, w), { create: !1, isFile: !0 }) instanceof FileSystemFileHandle; } async remove() { if (s(this, y)) throw Error("exists unclosed reader/writer"); await N(s(this, w)); } /** * If the target is a file, use current overwrite the target; * if the target is a folder, copy the current file into that folder. */ async copyTo(e) { if (!await this.exists()) throw Error(`file ${this.path} not exists`); if (e instanceof k) return m(e.path) === this ? this : (await V(e.path, this), m(e.path)); if (e instanceof C) return await this.copyTo(m(S(e.path, this.name))); throw Error("Illegal target type"); } /** * move file, copy then remove current */ async moveTo(e) { const t = await this.copyTo(e); return await this.remove(), t; } }; w = new WeakMap(), Z = new WeakMap(), F = new WeakMap(), y = new WeakMap(), v = new WeakMap(), W = new WeakMap(); let G = k; const U = "/.opfs-tools-temp-dir"; async function z(r) { try { if (r.kind === "file") { if (!await r.exists()) return !0; const e = await r.createWriter(); await e.truncate(0), await e.close(), await r.remove(); } else await r.remove(); return !0; } catch (e) { return console.warn(e), !1; } } function D() { setInterval(async () => { for (const e of await g(U).children()) { const t = /^\d+-(\d+)$/.exec(e.name); (t == null || Date.now() - Number(t[1]) > 2592e5) && await z(e); } }, 60 * 1e3); } const x = []; let P = !1; async function Q() { if (globalThis.localStorage == null) return; const r = "OPFS_TOOLS_EXPIRES_TMP_FILES"; P || (P = !0, globalThis.addEventListener("unload", () => { x.length !== 0 && localStorage.setItem( r, `${localStorage.getItem(r) ?? ""},${x.join(",")}` ); })); let e = localStorage.getItem(r) ?? ""; for (const t of e.split(",")) t.length !== 0 && await z(m(`${U}/${t}`)) && (e = e.replace(t, "")); localStorage.setItem(r, e.replace(/,{2,}/g, ",")); } (async function() { var e; globalThis.__opfs_tools_tmpfile_init__ !== !0 && (globalThis.__opfs_tools_tmpfile_init__ = !0, !(globalThis.FileSystemDirectoryHandle == null || globalThis.FileSystemFileHandle == null || ((e = globalThis.navigator) == null ? void 0 : e.storage.getDirectory) == null) && (D(), await Q())); })(); function A() { const r = `${Math.random().toString().slice(2)}-${Date.now()}`; return x.push(r), m(`${U}/${r}`); } function $(r, e) { let t = m(r), a = 0, i = t.createWriter(), n = t.createReader(); const c = async (l) => { const b = await (await n).read(a, { at: Math.round(a * 0.3) }); a = await l.write(b, { at: 0 }), await l.truncate(a); }; return { append: async (l) => { const o = await i; a += await o.write(l), a >= e && await c(o); }, text: t.text.bind(t), remove: async () => { await (await n).close(), await (await i).close(), await t.remove(); }, getSize: async () => a }; } export { g as dir, m as file, $ as rollfile, A as tmpfile, V as write }; //# sourceMappingURL=opfs-tools.js.map