UNPKG

@codesandbox/sandpack-client

Version:

<img style="width:100%" src="https://user-images.githubusercontent.com/4838076/143581035-ebee5ba2-9cb1-4fe8-a05b-2f44bd69bb4b.gif" alt="Component toolkit for live running code editing experiences" />

1,000 lines 38.6 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.UnlockedOverlayFS = void 0; var file_system_1 = require("../core/file_system"); var api_error_1 = require("../core/api_error"); var file_flag_1 = require("../core/file_flag"); var node_fs_stats_1 = require("../core/node_fs_stats"); var preload_file_1 = require("../generic/preload_file"); var locked_fs_1 = require("../generic/locked_fs"); var path = require("path"); /** * @hidden */ var deletionLogPath = '/.deletedFiles.log'; /** * Given a read-only mode, makes it writable. * @hidden */ function makeModeWritable(mode) { return 146 | mode; } /** * @hidden */ function getFlag(f) { return file_flag_1.FileFlag.getFileFlag(f); } /** * Overlays a RO file to make it writable. */ var OverlayFile = /** @class */ (function (_super) { __extends(OverlayFile, _super); function OverlayFile(fs, path, flag, stats, data) { return _super.call(this, fs, path, flag, stats, data) || this; } OverlayFile.prototype.sync = function (cb) { var _this = this; if (!this.isDirty()) { cb(null); return; } this._fs._syncAsync(this, function (err) { _this.resetDirty(); cb(err); }); }; OverlayFile.prototype.syncSync = function () { if (this.isDirty()) { this._fs._syncSync(this); this.resetDirty(); } }; OverlayFile.prototype.close = function (cb) { this.sync(cb); }; OverlayFile.prototype.closeSync = function () { this.syncSync(); }; return OverlayFile; }(preload_file_1.default)); /** * *INTERNAL, DO NOT USE DIRECTLY!* * * Core OverlayFS class that contains no locking whatsoever. We wrap these objects * in a LockedFS to prevent races. */ var UnlockedOverlayFS = /** @class */ (function (_super) { __extends(UnlockedOverlayFS, _super); function UnlockedOverlayFS(writable, readable) { var _this = _super.call(this) || this; _this._isInitialized = false; _this._initializeCallbacks = []; _this._deletedFiles = {}; _this._deleteLog = ''; // If 'true', we have scheduled a delete log update. _this._deleteLogUpdatePending = false; // If 'true', a delete log update is needed after the scheduled delete log // update finishes. _this._deleteLogUpdateNeeded = false; // If there was an error updating the delete log... _this._deleteLogError = null; _this._writable = writable; _this._readable = readable; if (_this._writable.isReadOnly()) { throw new api_error_1.ApiError(api_error_1.ErrorCode.EINVAL, "Writable file system must be writable."); } return _this; } UnlockedOverlayFS.isAvailable = function () { return true; }; UnlockedOverlayFS.prototype.getOverlayedFileSystems = function () { return { readable: this._readable, writable: this._writable }; }; UnlockedOverlayFS.prototype._syncAsync = function (file, cb) { var _this = this; this.createParentDirectoriesAsync(file.getPath(), function (err) { if (err) { return cb(err); } _this._writable.writeFile(file.getPath(), file.getBuffer(), null, getFlag('w'), file.getStats().mode, cb); }); }; UnlockedOverlayFS.prototype._syncSync = function (file) { this.createParentDirectories(file.getPath()); this._writable.writeFileSync(file.getPath(), file.getBuffer(), null, getFlag('w'), file.getStats().mode); }; UnlockedOverlayFS.prototype.getName = function () { return OverlayFS.Name; }; /** * **INTERNAL METHOD** * * Called once to load up metadata stored on the writable file system. */ UnlockedOverlayFS.prototype._initialize = function (cb) { var _this = this; var callbackArray = this._initializeCallbacks; var end = function (e) { _this._isInitialized = !e; _this._initializeCallbacks = []; callbackArray.forEach((function (cb) { return cb(e); })); }; // if we're already initialized, immediately invoke the callback if (this._isInitialized) { return cb(); } callbackArray.push(cb); // The first call to initialize initializes, the rest wait for it to complete. if (callbackArray.length !== 1) { return; } // Read deletion log, process into metadata. this._writable.readFile(deletionLogPath, 'utf8', getFlag('r'), function (err, data) { if (err) { // ENOENT === Newly-instantiated file system, and thus empty log. if (err.errno !== api_error_1.ErrorCode.ENOENT) { return end(err); } } else { _this._deleteLog = data; } _this._reparseDeletionLog(); end(); }); }; UnlockedOverlayFS.prototype.isReadOnly = function () { return false; }; UnlockedOverlayFS.prototype.supportsSynch = function () { return this._readable.supportsSynch() && this._writable.supportsSynch(); }; UnlockedOverlayFS.prototype.supportsLinks = function () { return false; }; UnlockedOverlayFS.prototype.supportsProps = function () { return this._readable.supportsProps() && this._writable.supportsProps(); }; UnlockedOverlayFS.prototype.getDeletionLog = function () { return this._deleteLog; }; UnlockedOverlayFS.prototype.restoreDeletionLog = function (log) { this._deleteLog = log; this._reparseDeletionLog(); this.updateLog(''); }; UnlockedOverlayFS.prototype.rename = function (oldPath, newPath, cb) { var _this = this; if (!this.checkInitAsync(cb) || this.checkPathAsync(oldPath, cb) || this.checkPathAsync(newPath, cb)) { return; } if (oldPath === deletionLogPath || newPath === deletionLogPath) { return cb(api_error_1.ApiError.EPERM('Cannot rename deletion log.')); } // nothing to do if paths match if (oldPath === newPath) { return cb(); } this.stat(oldPath, false, function (oldErr, oldStats) { if (oldErr) { return cb(oldErr); } return _this.stat(newPath, false, function (newErr, newStats) { var self = _this; // precondition: both oldPath and newPath exist and are dirs. // decreases: |files| // Need to move *every file/folder* currently stored on // readable to its new location on writable. function copyDirContents(files) { var file = files.shift(); if (!file) { return cb(); } var oldFile = path.resolve(oldPath, file); var newFile = path.resolve(newPath, file); // Recursion! Should work for any nested files / folders. self.rename(oldFile, newFile, function (err) { if (err) { return cb(err); } copyDirContents(files); }); } var mode = 511; // from linux's rename(2) manpage: oldpath can specify a // directory. In this case, newpath must either not exist, or // it must specify an empty directory. if (oldStats.isDirectory()) { if (newErr) { if (newErr.errno !== api_error_1.ErrorCode.ENOENT) { return cb(newErr); } return _this._writable.exists(oldPath, function (exists) { // simple case - both old and new are on the writable layer if (exists) { return _this._writable.rename(oldPath, newPath, cb); } _this._writable.mkdir(newPath, mode, function (mkdirErr) { if (mkdirErr) { return cb(mkdirErr); } _this._readable.readdir(oldPath, function (err, files) { if (err) { return cb(); } copyDirContents(files); }); }); }); } mode = newStats.mode; if (!newStats.isDirectory()) { return cb(api_error_1.ApiError.ENOTDIR(newPath)); } _this.readdir(newPath, function (readdirErr, files) { if (files && files.length) { return cb(api_error_1.ApiError.ENOTEMPTY(newPath)); } _this._readable.readdir(oldPath, function (err, files) { if (err) { return cb(); } copyDirContents(files); }); }); } if (newStats && newStats.isDirectory()) { return cb(api_error_1.ApiError.EISDIR(newPath)); } _this.readFile(oldPath, null, getFlag('r'), function (err, data) { if (err) { return cb(err); } return _this.writeFile(newPath, data, null, getFlag('w'), oldStats.mode, function (err) { if (err) { return cb(err); } return _this.unlink(oldPath, cb); }); }); }); }); }; UnlockedOverlayFS.prototype.renameSync = function (oldPath, newPath) { var _this = this; this.checkInitialized(); this.checkPath(oldPath); this.checkPath(newPath); if (oldPath === deletionLogPath || newPath === deletionLogPath) { throw api_error_1.ApiError.EPERM('Cannot rename deletion log.'); } // Write newPath using oldPath's contents, delete oldPath. var oldStats = this.statSync(oldPath, false); if (oldStats.isDirectory()) { // Optimization: Don't bother moving if old === new. if (oldPath === newPath) { return; } var mode = 511; if (this.existsSync(newPath)) { var stats = this.statSync(newPath, false); mode = stats.mode; if (stats.isDirectory()) { if (this.readdirSync(newPath).length > 0) { throw api_error_1.ApiError.ENOTEMPTY(newPath); } } else { throw api_error_1.ApiError.ENOTDIR(newPath); } } // Take care of writable first. Move any files there, or create an empty directory // if it doesn't exist. if (this._writable.existsSync(oldPath)) { this._writable.renameSync(oldPath, newPath); } else if (!this._writable.existsSync(newPath)) { this._writable.mkdirSync(newPath, mode); } // Need to move *every file/folder* currently stored on readable to its new location // on writable. if (this._readable.existsSync(oldPath)) { this._readable.readdirSync(oldPath).forEach(function (name) { // Recursion! Should work for any nested files / folders. _this.renameSync(path.resolve(oldPath, name), path.resolve(newPath, name)); }); } } else { if (this.existsSync(newPath) && this.statSync(newPath, false).isDirectory()) { throw api_error_1.ApiError.EISDIR(newPath); } this.writeFileSync(newPath, this.readFileSync(oldPath, null, getFlag('r')), null, getFlag('w'), oldStats.mode); } if (oldPath !== newPath && this.existsSync(oldPath)) { this.unlinkSync(oldPath); } }; UnlockedOverlayFS.prototype.stat = function (p, isLstat, cb) { var _this = this; if (!this.checkInitAsync(cb)) { return; } this._writable.stat(p, isLstat, function (err, stat) { if (err && err.errno === api_error_1.ErrorCode.ENOENT) { if (_this._deletedFiles[p]) { cb(api_error_1.ApiError.ENOENT(p)); } _this._readable.stat(p, isLstat, function (err, stat) { if (stat) { // Make the oldStat's mode writable. Preserve the topmost // part of the mode, which specifies if it is a file or a // directory. stat = node_fs_stats_1.default.clone(stat); stat.mode = makeModeWritable(stat.mode); } cb(err, stat); }); } else { cb(err, stat); } }); }; UnlockedOverlayFS.prototype.statSync = function (p, isLstat) { this.checkInitialized(); try { return this._writable.statSync(p, isLstat); } catch (e) { if (this._deletedFiles[p]) { throw api_error_1.ApiError.ENOENT(p); } var oldStat = node_fs_stats_1.default.clone(this._readable.statSync(p, isLstat)); // Make the oldStat's mode writable. Preserve the topmost part of the // mode, which specifies if it is a file or a directory. oldStat.mode = makeModeWritable(oldStat.mode); return oldStat; } }; UnlockedOverlayFS.prototype.open = function (p, flag, mode, cb) { var _this = this; if (!this.checkInitAsync(cb) || this.checkPathAsync(p, cb)) { return; } this.stat(p, false, function (err, stats) { if (stats) { switch (flag.pathExistsAction()) { case file_flag_1.ActionType.TRUNCATE_FILE: return _this.createParentDirectoriesAsync(p, function (err) { if (err) { return cb(err); } _this._writable.open(p, flag, mode, cb); }); case file_flag_1.ActionType.NOP: return _this._writable.exists(p, function (exists) { if (exists) { _this._writable.open(p, flag, mode, cb); } else { // at this point we know the stats object we got is from // the readable FS. stats = node_fs_stats_1.default.clone(stats); stats.mode = mode; _this._readable.readFile(p, null, getFlag('r'), function (readFileErr, data) { if (readFileErr) { return cb(readFileErr); } if (stats.size === -1) { stats.size = data.length; } var f = new OverlayFile(_this, p, flag, stats, data); cb(null, f); }); } }); default: return cb(api_error_1.ApiError.EEXIST(p)); } } else { switch (flag.pathNotExistsAction()) { case file_flag_1.ActionType.CREATE_FILE: return _this.createParentDirectoriesAsync(p, function (err) { if (err) { return cb(err); } return _this._writable.open(p, flag, mode, cb); }); default: return cb(api_error_1.ApiError.ENOENT(p)); } } }); }; UnlockedOverlayFS.prototype.openSync = function (p, flag, mode) { this.checkInitialized(); this.checkPath(p); if (p === deletionLogPath) { throw api_error_1.ApiError.EPERM('Cannot open deletion log.'); } if (this.existsSync(p)) { switch (flag.pathExistsAction()) { case file_flag_1.ActionType.TRUNCATE_FILE: this.createParentDirectories(p); return this._writable.openSync(p, flag, mode); case file_flag_1.ActionType.NOP: if (this._writable.existsSync(p)) { return this._writable.openSync(p, flag, mode); } else { // Create an OverlayFile. var buf = this._readable.readFileSync(p, null, getFlag('r')); var stats = node_fs_stats_1.default.clone(this._readable.statSync(p, false)); stats.mode = mode; return new OverlayFile(this, p, flag, stats, buf); } default: throw api_error_1.ApiError.EEXIST(p); } } else { switch (flag.pathNotExistsAction()) { case file_flag_1.ActionType.CREATE_FILE: this.createParentDirectories(p); return this._writable.openSync(p, flag, mode); default: throw api_error_1.ApiError.ENOENT(p); } } }; UnlockedOverlayFS.prototype.unlink = function (p, cb) { var _this = this; if (!this.checkInitAsync(cb) || this.checkPathAsync(p, cb)) { return; } this.exists(p, function (exists) { if (!exists) { return cb(api_error_1.ApiError.ENOENT(p)); } _this._writable.exists(p, function (writableExists) { if (writableExists) { return _this._writable.unlink(p, function (err) { if (err) { return cb(err); } _this.exists(p, function (readableExists) { if (readableExists) { _this.deletePath(p); } cb(null); }); }); } else { // if this only exists on the readable FS, add it to the // delete map. _this.deletePath(p); cb(null); } }); }); }; UnlockedOverlayFS.prototype.unlinkSync = function (p) { this.checkInitialized(); this.checkPath(p); if (this.existsSync(p)) { if (this._writable.existsSync(p)) { this._writable.unlinkSync(p); } // if it still exists add to the delete log if (this.existsSync(p)) { this.deletePath(p); } } else { throw api_error_1.ApiError.ENOENT(p); } }; UnlockedOverlayFS.prototype.rmdir = function (p, cb) { var _this = this; if (!this.checkInitAsync(cb)) { return; } var rmdirLower = function () { _this.readdir(p, function (err, files) { if (err) { return cb(err); } if (files.length) { return cb(api_error_1.ApiError.ENOTEMPTY(p)); } _this.deletePath(p); cb(null); }); }; this.exists(p, function (exists) { if (!exists) { return cb(api_error_1.ApiError.ENOENT(p)); } _this._writable.exists(p, function (writableExists) { if (writableExists) { _this._writable.rmdir(p, function (err) { if (err) { return cb(err); } _this._readable.exists(p, function (readableExists) { if (readableExists) { rmdirLower(); } else { cb(); } }); }); } else { rmdirLower(); } }); }); }; UnlockedOverlayFS.prototype.rmdirSync = function (p) { this.checkInitialized(); if (this.existsSync(p)) { if (this._writable.existsSync(p)) { this._writable.rmdirSync(p); } if (this.existsSync(p)) { // Check if directory is empty. if (this.readdirSync(p).length > 0) { throw api_error_1.ApiError.ENOTEMPTY(p); } else { this.deletePath(p); } } } else { throw api_error_1.ApiError.ENOENT(p); } }; UnlockedOverlayFS.prototype.mkdir = function (p, mode, cb) { var _this = this; if (!this.checkInitAsync(cb)) { return; } this.exists(p, function (exists) { if (exists) { return cb(api_error_1.ApiError.EEXIST(p)); } // The below will throw should any of the parent directories // fail to exist on _writable. _this.createParentDirectoriesAsync(p, function (err) { if (err) { return cb(err); } _this._writable.mkdir(p, mode, cb); }); }); }; UnlockedOverlayFS.prototype.mkdirSync = function (p, mode) { this.checkInitialized(); if (this.existsSync(p)) { throw api_error_1.ApiError.EEXIST(p); } else { // The below will throw should any of the parent directories fail to exist // on _writable. this.createParentDirectories(p); this._writable.mkdirSync(p, mode); } }; UnlockedOverlayFS.prototype.readdir = function (p, cb) { var _this = this; if (!this.checkInitAsync(cb)) { return; } this.stat(p, false, function (err, dirStats) { if (err) { return cb(err); } if (!dirStats.isDirectory()) { return cb(api_error_1.ApiError.ENOTDIR(p)); } _this._writable.readdir(p, function (err, wFiles) { if (err && err.code !== 'ENOENT') { return cb(err); } else if (err || !wFiles) { wFiles = []; } _this._readable.readdir(p, function (err, rFiles) { // if the directory doesn't exist on the lower FS set rFiles // here to simplify the following code. if (err || !rFiles) { rFiles = []; } // Readdir in both, check delete log on read-only file system's files, merge, return. var seenMap = {}; var filtered = wFiles.concat(rFiles.filter(function (fPath) { return !_this._deletedFiles["".concat(p, "/").concat(fPath)]; })).filter(function (fPath) { // Remove duplicates. var result = !seenMap[fPath]; seenMap[fPath] = true; return result; }); cb(null, filtered); }); }); }); }; UnlockedOverlayFS.prototype.readdirSync = function (p) { var _this = this; this.checkInitialized(); var dirStats = this.statSync(p, false); if (!dirStats.isDirectory()) { throw api_error_1.ApiError.ENOTDIR(p); } // Readdir in both, check delete log on RO file system's listing, merge, return. var contents = []; try { contents = contents.concat(this._writable.readdirSync(p)); } catch (e) { // NOP. } try { contents = contents.concat(this._readable.readdirSync(p).filter(function (fPath) { return !_this._deletedFiles["".concat(p, "/").concat(fPath)]; })); } catch (e) { // NOP. } var seenMap = {}; return contents.filter(function (fileP) { var result = !seenMap[fileP]; seenMap[fileP] = true; return result; }); }; UnlockedOverlayFS.prototype.exists = function (p, cb) { var _this = this; // Cannot pass an error back to callback, so throw an exception instead // if not initialized. this.checkInitialized(); this._writable.exists(p, function (existsWritable) { if (existsWritable) { return cb(true); } _this._readable.exists(p, function (existsReadable) { cb(existsReadable && _this._deletedFiles[p] !== true); }); }); }; UnlockedOverlayFS.prototype.existsSync = function (p) { this.checkInitialized(); return this._writable.existsSync(p) || (this._readable.existsSync(p) && this._deletedFiles[p] !== true); }; UnlockedOverlayFS.prototype.chmod = function (p, isLchmod, mode, cb) { var _this = this; if (!this.checkInitAsync(cb)) { return; } this.operateOnWritableAsync(p, function (err) { if (err) { return cb(err); } else { _this._writable.chmod(p, isLchmod, mode, cb); } }); }; UnlockedOverlayFS.prototype.chmodSync = function (p, isLchmod, mode) { var _this = this; this.checkInitialized(); this.operateOnWritable(p, function () { _this._writable.chmodSync(p, isLchmod, mode); }); }; UnlockedOverlayFS.prototype.chown = function (p, isLchmod, uid, gid, cb) { var _this = this; if (!this.checkInitAsync(cb)) { return; } this.operateOnWritableAsync(p, function (err) { if (err) { return cb(err); } else { _this._writable.chown(p, isLchmod, uid, gid, cb); } }); }; UnlockedOverlayFS.prototype.chownSync = function (p, isLchown, uid, gid) { var _this = this; this.checkInitialized(); this.operateOnWritable(p, function () { _this._writable.chownSync(p, isLchown, uid, gid); }); }; UnlockedOverlayFS.prototype.utimes = function (p, atime, mtime, cb) { var _this = this; if (!this.checkInitAsync(cb)) { return; } this.operateOnWritableAsync(p, function (err) { if (err) { return cb(err); } else { _this._writable.utimes(p, atime, mtime, cb); } }); }; UnlockedOverlayFS.prototype.utimesSync = function (p, atime, mtime) { var _this = this; this.checkInitialized(); this.operateOnWritable(p, function () { _this._writable.utimesSync(p, atime, mtime); }); }; UnlockedOverlayFS.prototype.deletePath = function (p) { this._deletedFiles[p] = true; this.updateLog("d".concat(p, "\n")); }; UnlockedOverlayFS.prototype.updateLog = function (addition) { var _this = this; this._deleteLog += addition; if (this._deleteLogUpdatePending) { this._deleteLogUpdateNeeded = true; } else { this._deleteLogUpdatePending = true; this._writable.writeFile(deletionLogPath, this._deleteLog, 'utf8', file_flag_1.FileFlag.getFileFlag('w'), 420, function (e) { _this._deleteLogUpdatePending = false; if (e) { _this._deleteLogError = e; } else if (_this._deleteLogUpdateNeeded) { _this._deleteLogUpdateNeeded = false; _this.updateLog(''); } }); } }; UnlockedOverlayFS.prototype._reparseDeletionLog = function () { var _this = this; this._deletedFiles = {}; this._deleteLog.split('\n').forEach(function (path) { // If the log entry begins w/ 'd', it's a deletion. _this._deletedFiles[path.slice(1)] = path.slice(0, 1) === 'd'; }); }; UnlockedOverlayFS.prototype.checkInitialized = function () { if (!this._isInitialized) { throw new api_error_1.ApiError(api_error_1.ErrorCode.EPERM, "OverlayFS is not initialized. Please initialize OverlayFS using its initialize() method before using it."); } else if (this._deleteLogError !== null) { var e = this._deleteLogError; this._deleteLogError = null; throw e; } }; UnlockedOverlayFS.prototype.checkInitAsync = function (cb) { if (!this._isInitialized) { cb(new api_error_1.ApiError(api_error_1.ErrorCode.EPERM, "OverlayFS is not initialized. Please initialize OverlayFS using its initialize() method before using it.")); return false; } else if (this._deleteLogError !== null) { var e = this._deleteLogError; this._deleteLogError = null; cb(e); return false; } return true; }; UnlockedOverlayFS.prototype.checkPath = function (p) { if (p === deletionLogPath) { throw api_error_1.ApiError.EPERM(p); } }; UnlockedOverlayFS.prototype.checkPathAsync = function (p, cb) { if (p === deletionLogPath) { cb(api_error_1.ApiError.EPERM(p)); return true; } return false; }; UnlockedOverlayFS.prototype.createParentDirectoriesAsync = function (p, cb) { var parent = path.dirname(p); var toCreate = []; var self = this; this._writable.stat(parent, false, statDone); function statDone(err, stat) { if (err) { if (parent === "/") { cb(new api_error_1.ApiError(api_error_1.ErrorCode.EBUSY, "Invariant failed: root does not exist!")); } else { toCreate.push(parent); parent = path.dirname(parent); self._writable.stat(parent, false, statDone); } } else { createParents(); } } function createParents() { if (!toCreate.length) { return cb(); } var dir = toCreate.pop(); self._readable.stat(dir, false, function (err, stats) { // stop if we couldn't read the dir if (!stats) { return cb(); } self._writable.mkdir(dir, stats.mode, function (err) { if (err) { return cb(err); } createParents(); }); }); } }; /** * With the given path, create the needed parent directories on the writable storage * should they not exist. Use modes from the read-only storage. */ UnlockedOverlayFS.prototype.createParentDirectories = function (p) { var _this = this; var parent = path.dirname(p), toCreate = []; while (!this._writable.existsSync(parent)) { toCreate.push(parent); parent = path.dirname(parent); } toCreate = toCreate.reverse(); toCreate.forEach(function (p) { _this._writable.mkdirSync(p, _this.statSync(p, false).mode); }); }; /** * Helper function: * - Ensures p is on writable before proceeding. Throws an error if it doesn't exist. * - Calls f to perform operation on writable. */ UnlockedOverlayFS.prototype.operateOnWritable = function (p, f) { if (this.existsSync(p)) { if (!this._writable.existsSync(p)) { // File is on readable storage. Copy to writable storage before // changing its mode. this.copyToWritable(p); } f(); } else { throw api_error_1.ApiError.ENOENT(p); } }; UnlockedOverlayFS.prototype.operateOnWritableAsync = function (p, cb) { var _this = this; this.exists(p, function (exists) { if (!exists) { return cb(api_error_1.ApiError.ENOENT(p)); } _this._writable.exists(p, function (existsWritable) { if (existsWritable) { cb(); } else { return _this.copyToWritableAsync(p, cb); } }); }); }; /** * Copy from readable to writable storage. * PRECONDITION: File does not exist on writable storage. */ UnlockedOverlayFS.prototype.copyToWritable = function (p) { var pStats = this.statSync(p, false); if (pStats.isDirectory()) { this._writable.mkdirSync(p, pStats.mode); } else { this.writeFileSync(p, this._readable.readFileSync(p, null, getFlag('r')), null, getFlag('w'), this.statSync(p, false).mode); } }; UnlockedOverlayFS.prototype.copyToWritableAsync = function (p, cb) { var _this = this; this.stat(p, false, function (err, pStats) { if (err) { return cb(err); } if (pStats.isDirectory()) { return _this._writable.mkdir(p, pStats.mode, cb); } // need to copy file. _this._readable.readFile(p, null, getFlag('r'), function (err, data) { if (err) { return cb(err); } _this.writeFile(p, data, null, getFlag('w'), pStats.mode, cb); }); }); }; return UnlockedOverlayFS; }(file_system_1.BaseFileSystem)); exports.UnlockedOverlayFS = UnlockedOverlayFS; /** * OverlayFS makes a read-only filesystem writable by storing writes on a second, * writable file system. Deletes are persisted via metadata stored on the writable * file system. */ var OverlayFS = /** @class */ (function (_super) { __extends(OverlayFS, _super); /** * @param writable The file system to write modified files to. * @param readable The file system that initially populates this file system. */ function OverlayFS(writable, readable) { return _super.call(this, new UnlockedOverlayFS(writable, readable)) || this; } /** * Constructs and initializes an OverlayFS instance with the given options. */ OverlayFS.Create = function (opts, cb) { try { var fs_1 = new OverlayFS(opts.writable, opts.readable); fs_1._initialize(function (e) { cb(e, fs_1); }); } catch (e) { cb(e); } }; OverlayFS.isAvailable = function () { return UnlockedOverlayFS.isAvailable(); }; OverlayFS.prototype.getOverlayedFileSystems = function () { return _super.prototype.getFSUnlocked.call(this).getOverlayedFileSystems(); }; OverlayFS.prototype.unwrap = function () { return _super.prototype.getFSUnlocked.call(this); }; OverlayFS.prototype._initialize = function (cb) { _super.prototype.getFSUnlocked.call(this)._initialize(cb); }; OverlayFS.Name = "OverlayFS"; OverlayFS.Options = { writable: { type: "object", description: "The file system to write modified files to." }, readable: { type: "object", description: "The file system that initially populates this file system." } }; return OverlayFS; }(locked_fs_1.default)); exports.default = OverlayFS;