one
Version:
One is a React framework for web, iOS, and Android apps.
108 lines (106 loc) • 3.52 kB
JavaScript
const folders = [
"documents",
"cache",
"applicationSupport",
"temporary"
];
const directories = Object.fromEntries(folders.map((name) => [name, `opfs://one/${name}/`]));
function parts(uri) {
const url = new URL(uri);
if (url.protocol !== "opfs:" || url.host !== "one" || url.search || url.hash) {
throw new Error("FileSystem: expected an opfs://one/ URI");
}
const names = url.pathname.split("/").filter(Boolean).map(decodeURIComponent);
if (!folders.includes(names[0]) || names.some((name) => name === "." || name === ".." || /[/\\\0]/.test(name))) {
throw new Error("FileSystem: invalid path");
}
return names;
}
async function root() {
if (!navigator.storage?.getDirectory) throw new Error("FileSystem: Origin Private File System is unavailable");
const handle = await navigator.storage.getDirectory();
for (const name of folders) await handle.getDirectoryHandle(name, { create: true });
return handle;
}
async function parent(names, create = false) {
let handle = await root();
for (const name of names.slice(0, -1)) handle = await handle.getDirectoryHandle(name, { create });
return handle;
}
async function entry(names) {
const handle = await parent(names);
const name = names.at(-1);
try {
return await handle.getFileHandle(name);
} catch (error) {
if (error.name !== "TypeMismatchError") throw error;
return handle.getDirectoryHandle(name);
}
}
async function info(uri) {
const names = parts(uri);
try {
const handle = await entry(names);
if (handle.kind === "directory") return {
uri,
exists: true,
isDirectory: true
};
const file = await handle.getFile();
return {
uri,
exists: true,
isDirectory: false,
size: file.size,
modifiedAt: file.lastModified
};
} catch (error) {
if (error.name !== "NotFoundError") throw error;
return {
uri,
exists: false,
isDirectory: false
};
}
}
async function write(handle, data) {
const writable = await handle.createWritable();
try {
await writable.write(data);
await writable.close();
} catch (error) {
await writable.abort().catch(() => {});
throw error;
}
}
async function copyEntry(source, target, name) {
if (source.kind === "file") {
await write(await target.getFileHandle(name, { create: true }), await source.getFile());
} else {
const directory = await target.getDirectoryHandle(name, { create: true });
for await (const [childName, child] of source.entries()) await copyEntry(child, directory, childName);
}
}
async function transfer(fromUri, toUri, move) {
const from = parts(fromUri), to = parts(toUri);
if (move && from.length === 1) throw new Error("FileSystem: app root directories are protected");
if (to.length <= from.length && to.every((name, i) => name === from[i]) || from.every((name, i) => name === to[i])) throw new Error("FileSystem: overlapping paths");
const source = await entry(from);
if ((await info(toUri)).exists) throw new Error("FileSystem: destination already exists");
const destination = await parent(to);
try {
await copyEntry(source, destination, to.at(-1));
} catch (error) {
await destination.removeEntry(to.at(-1), { recursive: true }).catch(() => {});
throw error;
}
if (move) await (await parent(from)).removeEntry(from.at(-1), { recursive: true });
}
let tail = Promise.resolve();
function serial(work) {
const next = tail.then(work);
tail = next.catch(() => {});
return next;
}
export { directories, entry, folders, info, parent, parts, root, serial, transfer, write };
//# sourceMappingURL=opfs.mjs.map