var cellfs = {} // CellFS: A filesystem implementation using miniz and raw OS filesystem // Supports mounting multiple sources (fs, zip) and named mounts (@name) var fd = use('fd') var miniz = use('miniz') var qop = use('qop') var wildstar = use('wildstar') // Internal state var mounts = [] // Array of {source, type, handle, name} var writepath = "." // Helper to normalize paths function normalize_path(path) { if (!path) return "" // Remove leading/trailing slashes and normalize return replace(path, /^\/+|\/+$/, "") } // Check if a file exists in a specific mount function mount_exists(mount, path) { if (mount.type == 'zip') { try { mount.handle.mod(path) return true } catch (e) { return false } } else if (mount.type == 'qop') { try { return mount.handle.stat(path) != null } catch (e) { return false } } else { // fs var full_path = fd.join_paths(mount.source, path) try { var st = fd.stat(full_path) return st.isFile || st.isDirectory } catch (e) { return false } } } // Check if a path refers to a directory in a specific mount function is_directory(path) { var res = resolve(path) var mount = res.mount if (mount.type == 'zip') { try { return mount.handle.is_directory(path); } catch (e) { return false; } } else if (mount.type == 'qop') { try { return mount.handle.is_directory(path); } catch (e) { return false; } } else { // fs var full_path = fd.join_paths(mount.source, path) try { var st = fd.stat(full_path) return st.isDirectory } catch (e) { return false } } } // Resolve a path to a specific mount and relative path // Returns { mount, path } or throws/returns null function resolve(path, must_exist) { path = normalize_path(path) // Check for named mount if (starts_with(path, "@")) { var idx = search(path, "/") var mount_name = "" var rel_path = "" if (idx == null) { mount_name = text(path, 1) rel_path = "" } else { mount_name = text(path, 1, idx) rel_path = text(path, idx + 1) } // Find named mount var mount = null for (var m of mounts) { if (m.name == mount_name) { mount = m break } } if (!mount) { throw Error("Unknown mount point: @" + mount_name) } return { mount: mount, path: rel_path } } // Search path for (var mount of mounts) { if (mount_exists(mount, path)) { return { mount: mount, path: path } } } if (must_exist) { throw Error("File not found in any mount: " + path) } } // Mount a source function mount(source, name) { // Check if source exists var st = fd.stat(source) var mount_info = { source: source, name: name || null, type: 'fs', handle: null, zip_blob: null } if (st.isDirectory) { mount_info.type = 'fs' } else if (st.isFile) { var blob = fd.slurp(source) // Try QOP first (it's likely faster to fail?) or Zip? // QOP open checks magic. var qop_archive = null try { qop_archive = qop.open(blob) } catch(e) {} if (qop_archive) { mount_info.type = 'qop' mount_info.handle = qop_archive mount_info.zip_blob = blob // keep blob alive } else { var zip = miniz.read(blob) if (!is_object(zip) || !is_function(zip.count)) { throw Error("Invalid archive file (not zip or qop): " + source) } mount_info.type = 'zip' mount_info.handle = zip mount_info.zip_blob = blob // keep blob alive } } else { throw Error("Unsupported mount source type: " + source) } mounts.push(mount_info) } // Unmount function unmount(name_or_source) { for (var i = 0; i < mounts.length; i++) { if (mounts[i].name == name_or_source || mounts[i].source == name_or_source) { mounts.splice(i, 1) return } } throw Error("Mount not found: " + name_or_source) } // Read file function slurp(path) { var res = resolve(path, true) if (!res) throw Error("File not found: " + path) if (res.mount.type == 'zip') { return res.mount.handle.slurp(res.path) } else if (res.mount.type == 'qop') { var data = res.mount.handle.read(res.path) if (!data) throw Error("File not found in qop: " + path) return data } else { var full_path = fd.join_paths(res.mount.source, res.path) return fd.slurp(full_path) } } // Write file function slurpwrite(path, data) { var full_path = writepath + "/" + path var f = fd.open(full_path, 'w') fd.write(f, data) fd.close(f) } // Check existence function exists(path) { var res = resolve(path, false) if (starts_with(path, "@")) { return mount_exists(res.mount, res.path) } return res != null } // Stat function stat(path) { var res = resolve(path, true) if (!res) throw Error("File not found: " + path) if (res.mount.type == 'zip') { var mod = res.mount.handle.mod(res.path) return { filesize: 0, modtime: mod * 1000, isDirectory: false } } else if (res.mount.type == 'qop') { var s = res.mount.handle.stat(res.path) if (!s) throw Error("File not found in qop: " + path) return { filesize: s.size, modtime: s.modtime, isDirectory: s.isDirectory } } else { var full_path = fd.join_paths(res.mount.source, res.path) var s = fd.stat(full_path) return { filesize: s.size, modtime: s.mtime, isDirectory: s.isDirectory } } } // Get search paths function searchpath() { return array(mounts) } // Mount a package using the shop system function mount_package(name) { if (name == null) { mount('.', null) return } var shop = use('internal/shop') var dir = shop.get_package_dir(name) if (!dir) { throw Error("Package not found: " + name) } mount(dir, name) } // New functions for qjs_io compatibility function match(str, pattern) { return wildstar.match(pattern, str, wildstar.WM_PATHNAME | wildstar.WM_PERIOD | wildstar.WM_WILDSTAR) } function rm(path) { var res = resolve(path, true) if (res.mount.type != 'fs') throw Error("Cannot delete from non-fs mount") var full_path = fd.join_paths(res.mount.source, res.path) var st = fd.stat(full_path) if (st.isDirectory) fd.rmdir(full_path) else fd.unlink(full_path) } function mkdir(path) { var full = fd.join_paths(writepath, path) fd.mkdir(full) } function set_writepath(path) { writepath = path } function basedir() { return fd.getcwd() } function prefdir(org, app) { return "./" } function realdir(path) { var res = resolve(path, false) if (!res) return null return fd.join_paths(res.mount.source, res.path) } function enumerate(path, recurse) { if (path == null) path = "" var res = resolve(path, true) var results = [] function visit(curr_full, rel_prefix) { var list = fd.readdir(curr_full) if (!list) return for (var item of list) { var item_rel = rel_prefix ? rel_prefix + "/" + item : item results.push(item_rel) if (recurse) { var st = fd.stat(fd.join_paths(curr_full, item)) if (st.isDirectory) { visit(fd.join_paths(curr_full, item), item_rel) } } } } if (res.mount.type == 'fs') { var full = fd.join_paths(res.mount.source, res.path) var st = fd.stat(full) if (st && st.isDirectory) { visit(full, "") } } else if (res.mount.type == 'qop') { var all = res.mount.handle.list() var prefix = res.path ? res.path + "/" : "" var prefix_len = prefix.length // Use a set to avoid duplicates if we are simulating directories var seen = {} for (var p of all) { if (starts_with(p, prefix)) { var rel = text(p, prefix_len) if (rel.length == 0) continue if (!recurse) { var slash = search(rel, '/') if (slash != null) { rel = text(rel, 0, slash) } } if (!seen[rel]) { seen[rel] = true results.push(rel) } } } } return results } function globfs(globs, dir) { if (dir == null) dir = "" var res = resolve(dir, true) var results = [] function check_neg(path) { for (var g of globs) { if (starts_with(g, "!") && wildstar.match(text(g, 1), path, wildstar.WM_WILDSTAR)) return true; } return false; } function check_pos(path) { for (var g of globs) { if (!starts_with(g, "!") && wildstar.match(g, path, wildstar.WM_WILDSTAR)) return true; } return false; } function visit(curr_full, rel_prefix) { if (rel_prefix && check_neg(rel_prefix)) return var list = fd.readdir(curr_full) if (!list) return for (var item of list) { var item_rel = rel_prefix ? rel_prefix + "/" + item : item var child_full = fd.join_paths(curr_full, item) var st = fd.stat(child_full) if (st.isDirectory) { if (!check_neg(item_rel)) { visit(child_full, item_rel) } } else { if (!check_neg(item_rel) && check_pos(item_rel)) { results.push(item_rel) } } } } if (res.mount.type == 'fs') { var full = fd.join_paths(res.mount.source, res.path) var st = fd.stat(full) if (st && st.isDirectory) { visit(full, "") } } else if (res.mount.type == 'qop') { var all = res.mount.handle.list() var prefix = res.path ? res.path + "/" : "" var prefix_len = prefix.length for (var p of all) { if (starts_with(p, prefix)) { var rel = text(p, prefix_len) if (rel.length == 0) continue if (!check_neg(rel) && check_pos(rel)) { results.push(rel) } } } } return results } // Exports cellfs.mount = mount cellfs.mount_package = mount_package cellfs.unmount = unmount cellfs.slurp = slurp cellfs.slurpwrite = slurpwrite cellfs.exists = exists cellfs.is_directory = is_directory cellfs.stat = stat cellfs.searchpath = searchpath cellfs.match = match cellfs.enumerate = enumerate cellfs.globfs = globfs cellfs.rm = rm cellfs.mkdir = mkdir cellfs.writepath = set_writepath cellfs.basedir = basedir cellfs.prefdir = prefdir cellfs.realdir = realdir cellfs.mount('.') return cellfs