import { promises, existsSync } from 'node:fs'; import { resolve, dirname, join } from 'node:path'; function defineDriver(factory) { return factory; } function createError(driver, message, opts) { const err = new Error(`[unstorage] [${driver}] ${message}`, opts); if (Error.captureStackTrace) { Error.captureStackTrace(err, createError); } return err; } function createRequiredError(driver, name) { if (Array.isArray(name)) { return createError( driver, `Missing some of the required options ${name.map((n) => "`" + n + "`").join(", ")}` ); } return createError(driver, `Missing required option \`${name}\`.`); } function ignoreNotfound(err) { return err.code === "ENOENT" || err.code === "EISDIR" ? null : err; } function ignoreExists(err) { return err.code === "EEXIST" ? null : err; } async function writeFile(path, data, encoding) { await ensuredir(dirname(path)); return promises.writeFile(path, data, encoding); } function readFile(path, encoding) { return promises.readFile(path, encoding).catch(ignoreNotfound); } function unlink(path) { return promises.unlink(path).catch(ignoreNotfound); } function readdir(dir) { return promises.readdir(dir, { withFileTypes: true }).catch(ignoreNotfound).then((r) => r || []); } async function ensuredir(dir) { if (existsSync(dir)) { return; } await ensuredir(dirname(dir)).catch(ignoreExists); await promises.mkdir(dir).catch(ignoreExists); } async function readdirRecursive(dir, ignore, maxDepth) { if (ignore && ignore(dir)) { return []; } const entries = await readdir(dir); const files = []; await Promise.all( entries.map(async (entry) => { const entryPath = resolve(dir, entry.name); if (entry.isDirectory()) { if (maxDepth === void 0 || maxDepth > 0) { const dirFiles = await readdirRecursive( entryPath, ignore, maxDepth === void 0 ? void 0 : maxDepth - 1 ); files.push(...dirFiles.map((f) => entry.name + "/" + f)); } } else { if (!(ignore && ignore(entry.name))) { files.push(entry.name); } } }) ); return files; } async function rmRecursive(dir) { const entries = await readdir(dir); await Promise.all( entries.map((entry) => { const entryPath = resolve(dir, entry.name); if (entry.isDirectory()) { return rmRecursive(entryPath).then(() => promises.rmdir(entryPath)); } else { return promises.unlink(entryPath); } }) ); } const PATH_TRAVERSE_RE = /\.\.:|\.\.$/; const DRIVER_NAME = "fs-lite"; const fsLite = defineDriver((opts = {}) => { if (!opts.base) { throw createRequiredError(DRIVER_NAME, "base"); } opts.base = resolve(opts.base); const r = (key) => { if (PATH_TRAVERSE_RE.test(key)) { throw createError( DRIVER_NAME, `Invalid key: ${JSON.stringify(key)}. It should not contain .. segments` ); } const resolved = join(opts.base, key.replace(/:/g, "/")); return resolved; }; return { name: DRIVER_NAME, options: opts, flags: { maxDepth: true }, hasItem(key) { return existsSync(r(key)); }, getItem(key) { return readFile(r(key), "utf8"); }, getItemRaw(key) { return readFile(r(key)); }, async getMeta(key) { const { atime, mtime, size, birthtime, ctime } = await promises.stat(r(key)).catch(() => ({})); return { atime, mtime, size, birthtime, ctime }; }, setItem(key, value) { if (opts.readOnly) { return; } return writeFile(r(key), value, "utf8"); }, setItemRaw(key, value) { if (opts.readOnly) { return; } return writeFile(r(key), value); }, removeItem(key) { if (opts.readOnly) { return; } return unlink(r(key)); }, getKeys(_base, topts) { return readdirRecursive(r("."), opts.ignore, topts?.maxDepth); }, async clear() { if (opts.readOnly || opts.noClear) { return; } await rmRecursive(r(".")); } }; }); export { fsLite as default };