@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,573 lines (1,518 loc) • 756 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["BrowserFS"] = factory();
else
root["BrowserFS"] = factory();
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./build/temp/library/rollup/browserfs.rollup.js");
/******/ })
/************************************************************************/
/******/ ({
/***/ "./build/temp/library/rollup/browserfs.rollup.js":
/*!*******************************************************!*\
!*** ./build/temp/library/rollup/browserfs.rollup.js ***!
\*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(Buffer, global, process) {
Object.defineProperty(exports, '__esModule', { value: true });
var buffer = __webpack_require__(/*! buffer */ "./node_modules/buffer/index.js");
var path = __webpack_require__(/*! path */ "./node_modules/bfs-path/js/path.js");
/**
* Standard libc error codes. Add more to this enum and ErrorStrings as they are
* needed.
* @url http://www.gnu.org/software/libc/manual/html_node/Error-Codes.html
*/
var ErrorCode;
(function (ErrorCode) {
ErrorCode[ErrorCode["EPERM"] = 1] = "EPERM";
ErrorCode[ErrorCode["ENOENT"] = 2] = "ENOENT";
ErrorCode[ErrorCode["EIO"] = 5] = "EIO";
ErrorCode[ErrorCode["EBADF"] = 9] = "EBADF";
ErrorCode[ErrorCode["EACCES"] = 13] = "EACCES";
ErrorCode[ErrorCode["EBUSY"] = 16] = "EBUSY";
ErrorCode[ErrorCode["EEXIST"] = 17] = "EEXIST";
ErrorCode[ErrorCode["ENOTDIR"] = 20] = "ENOTDIR";
ErrorCode[ErrorCode["EISDIR"] = 21] = "EISDIR";
ErrorCode[ErrorCode["EINVAL"] = 22] = "EINVAL";
ErrorCode[ErrorCode["EFBIG"] = 27] = "EFBIG";
ErrorCode[ErrorCode["ENOSPC"] = 28] = "ENOSPC";
ErrorCode[ErrorCode["EROFS"] = 30] = "EROFS";
ErrorCode[ErrorCode["ENOTEMPTY"] = 39] = "ENOTEMPTY";
ErrorCode[ErrorCode["ENOTSUP"] = 95] = "ENOTSUP";
})(ErrorCode || (ErrorCode = {}));
/* tslint:disable:variable-name */
/**
* Strings associated with each error code.
* @hidden
*/
var ErrorStrings = {};
ErrorStrings[ErrorCode.EPERM] = 'Operation not permitted.';
ErrorStrings[ErrorCode.ENOENT] = 'No such file or directory.';
ErrorStrings[ErrorCode.EIO] = 'Input/output error.';
ErrorStrings[ErrorCode.EBADF] = 'Bad file descriptor.';
ErrorStrings[ErrorCode.EACCES] = 'Permission denied.';
ErrorStrings[ErrorCode.EBUSY] = 'Resource busy or locked.';
ErrorStrings[ErrorCode.EEXIST] = 'File exists.';
ErrorStrings[ErrorCode.ENOTDIR] = 'File is not a directory.';
ErrorStrings[ErrorCode.EISDIR] = 'File is a directory.';
ErrorStrings[ErrorCode.EINVAL] = 'Invalid argument.';
ErrorStrings[ErrorCode.EFBIG] = 'File is too big.';
ErrorStrings[ErrorCode.ENOSPC] = 'No space left on disk.';
ErrorStrings[ErrorCode.EROFS] = 'Cannot modify a read-only file system.';
ErrorStrings[ErrorCode.ENOTEMPTY] = 'Directory is not empty.';
ErrorStrings[ErrorCode.ENOTSUP] = 'Operation is not supported.';
/* tslint:enable:variable-name */
/**
* Represents a BrowserFS error. Passed back to applications after a failed
* call to the BrowserFS API.
*/
var ApiError = /*@__PURE__*/(function (Error) {
function ApiError(type, message, path) {
if ( message === void 0 ) message = ErrorStrings[type];
Error.call(this, message);
// Unsupported.
this.syscall = "";
this.errno = type;
this.code = ErrorCode[type];
this.path = path;
this.stack = new Error().stack;
this.message = "Error: " + (this.code) + ": " + message + (this.path ? (", '" + (this.path) + "'") : '');
}
if ( Error ) ApiError.__proto__ = Error;
ApiError.prototype = Object.create( Error && Error.prototype );
ApiError.prototype.constructor = ApiError;
/**
* @return A friendly error message.
*/
ApiError.fromJSON = function fromJSON (json) {
var err = new ApiError(0);
err.errno = json.errno;
err.code = json.code;
err.path = json.path;
err.stack = json.stack;
err.message = json.message;
return err;
};
/**
* Creates an ApiError object from a buffer.
*/
ApiError.fromBuffer = function fromBuffer (buffer, i) {
if ( i === void 0 ) i = 0;
return ApiError.fromJSON(JSON.parse(buffer.toString('utf8', i + 4, i + 4 + buffer.readUInt32LE(i))));
};
ApiError.FileError = function FileError (code, p) {
return new ApiError(code, ErrorStrings[code], p);
};
ApiError.ENOENT = function ENOENT (path) {
return this.FileError(ErrorCode.ENOENT, path);
};
ApiError.EEXIST = function EEXIST (path) {
return this.FileError(ErrorCode.EEXIST, path);
};
ApiError.EISDIR = function EISDIR (path) {
return this.FileError(ErrorCode.EISDIR, path);
};
ApiError.ENOTDIR = function ENOTDIR (path) {
return this.FileError(ErrorCode.ENOTDIR, path);
};
ApiError.EPERM = function EPERM (path) {
return this.FileError(ErrorCode.EPERM, path);
};
ApiError.ENOTEMPTY = function ENOTEMPTY (path) {
return this.FileError(ErrorCode.ENOTEMPTY, path);
};
ApiError.prototype.toString = function toString () {
return this.message;
};
ApiError.prototype.toJSON = function toJSON () {
return {
errno: this.errno,
code: this.code,
path: this.path,
stack: this.stack,
message: this.message
};
};
/**
* Writes the API error into a buffer.
*/
ApiError.prototype.writeToBuffer = function writeToBuffer (buffer, i) {
if ( buffer === void 0 ) buffer = Buffer.alloc(this.bufferSize());
if ( i === void 0 ) i = 0;
var bytesWritten = buffer.write(JSON.stringify(this.toJSON()), i + 4);
buffer.writeUInt32LE(bytesWritten, i);
return buffer;
};
/**
* The size of the API error in buffer-form in bytes.
*/
ApiError.prototype.bufferSize = function bufferSize () {
// 4 bytes for string length.
return 4 + Buffer.byteLength(JSON.stringify(this.toJSON()));
};
return ApiError;
}(Error));
var api_error = /*#__PURE__*/Object.freeze({
get ErrorCode () { return ErrorCode; },
ErrorStrings: ErrorStrings,
ApiError: ApiError
});
var ActionType;
(function (ActionType) {
// Indicates that the code should not do anything.
ActionType[ActionType["NOP"] = 0] = "NOP";
// Indicates that the code should throw an exception.
ActionType[ActionType["THROW_EXCEPTION"] = 1] = "THROW_EXCEPTION";
// Indicates that the code should truncate the file, but only if it is a file.
ActionType[ActionType["TRUNCATE_FILE"] = 2] = "TRUNCATE_FILE";
// Indicates that the code should create the file.
ActionType[ActionType["CREATE_FILE"] = 3] = "CREATE_FILE";
})(ActionType || (ActionType = {}));
/**
* Represents one of the following file flags. A convenience object.
*
* * `'r'` - Open file for reading. An exception occurs if the file does not exist.
* * `'r+'` - Open file for reading and writing. An exception occurs if the file does not exist.
* * `'rs'` - Open file for reading in synchronous mode. Instructs the filesystem to not cache writes.
* * `'rs+'` - Open file for reading and writing, and opens the file in synchronous mode.
* * `'w'` - Open file for writing. The file is created (if it does not exist) or truncated (if it exists).
* * `'wx'` - Like 'w' but opens the file in exclusive mode.
* * `'w+'` - Open file for reading and writing. The file is created (if it does not exist) or truncated (if it exists).
* * `'wx+'` - Like 'w+' but opens the file in exclusive mode.
* * `'a'` - Open file for appending. The file is created if it does not exist.
* * `'ax'` - Like 'a' but opens the file in exclusive mode.
* * `'a+'` - Open file for reading and appending. The file is created if it does not exist.
* * `'ax+'` - Like 'a+' but opens the file in exclusive mode.
*
* Exclusive mode ensures that the file path is newly created.
*/
var FileFlag = function FileFlag(flagStr) {
this.flagStr = flagStr;
if (FileFlag.validFlagStrs.indexOf(flagStr) < 0) {
throw new ApiError(ErrorCode.EINVAL, "Invalid flag: " + flagStr);
}
};
/**
* Get the underlying flag string for this flag.
*/
FileFlag.getFileFlag = function getFileFlag (flagStr) {
// Check cache first.
if (FileFlag.flagCache.hasOwnProperty(flagStr)) {
return FileFlag.flagCache[flagStr];
}
return FileFlag.flagCache[flagStr] = new FileFlag(flagStr);
};
FileFlag.prototype.getFlagString = function getFlagString () {
return this.flagStr;
};
/**
* Returns true if the file is readable.
*/
FileFlag.prototype.isReadable = function isReadable () {
return this.flagStr.indexOf('r') !== -1 || this.flagStr.indexOf('+') !== -1;
};
/**
* Returns true if the file is writeable.
*/
FileFlag.prototype.isWriteable = function isWriteable () {
return this.flagStr.indexOf('w') !== -1 || this.flagStr.indexOf('a') !== -1 || this.flagStr.indexOf('+') !== -1;
};
/**
* Returns true if the file mode should truncate.
*/
FileFlag.prototype.isTruncating = function isTruncating () {
return this.flagStr.indexOf('w') !== -1;
};
/**
* Returns true if the file is appendable.
*/
FileFlag.prototype.isAppendable = function isAppendable () {
return this.flagStr.indexOf('a') !== -1;
};
/**
* Returns true if the file is open in synchronous mode.
*/
FileFlag.prototype.isSynchronous = function isSynchronous () {
return this.flagStr.indexOf('s') !== -1;
};
/**
* Returns true if the file is open in exclusive mode.
*/
FileFlag.prototype.isExclusive = function isExclusive () {
return this.flagStr.indexOf('x') !== -1;
};
/**
* Returns one of the static fields on this object that indicates the
* appropriate response to the path existing.
*/
FileFlag.prototype.pathExistsAction = function pathExistsAction () {
if (this.isExclusive()) {
return ActionType.THROW_EXCEPTION;
}
else if (this.isTruncating()) {
return ActionType.TRUNCATE_FILE;
}
else {
return ActionType.NOP;
}
};
/**
* Returns one of the static fields on this object that indicates the
* appropriate response to the path not existing.
*/
FileFlag.prototype.pathNotExistsAction = function pathNotExistsAction () {
if ((this.isWriteable() || this.isAppendable()) && this.flagStr !== 'r+') {
return ActionType.CREATE_FILE;
}
else {
return ActionType.THROW_EXCEPTION;
}
};
// Contains cached FileMode instances.
FileFlag.flagCache = {};
// Array of valid mode strings.
FileFlag.validFlagStrs = ['r', 'r+', 'rs', 'rs+', 'w', 'wx', 'w+', 'wx+', 'a', 'ax', 'a+', 'ax+'];
/**
* Indicates the type of the given file. Applied to 'mode'.
*/
var FileType;
(function (FileType) {
FileType[FileType["FILE"] = 32768] = "FILE";
FileType[FileType["DIRECTORY"] = 16384] = "DIRECTORY";
FileType[FileType["SYMLINK"] = 40960] = "SYMLINK";
})(FileType || (FileType = {}));
/**
* Emulation of Node's `fs.Stats` object.
*
* Attribute descriptions are from `man 2 stat'
* @see http://nodejs.org/api/fs.html#fs_class_fs_stats
* @see http://man7.org/linux/man-pages/man2/stat.2.html
*/
var Stats = function Stats(itemType, size, mode, atimeMs, mtimeMs, ctimeMs, birthtimeMs) {
/**
* UNSUPPORTED ATTRIBUTES
* I assume no one is going to need these details, although we could fake
* appropriate values if need be.
*/
// ID of device containing file
this.dev = 0;
// inode number
this.ino = 0;
// device ID (if special file)
this.rdev = 0;
// number of hard links
this.nlink = 1;
// blocksize for file system I/O
this.blksize = 4096;
// @todo Maybe support these? atm, it's a one-user filesystem.
// user ID of owner
this.uid = 0;
// group ID of owner
this.gid = 0;
// XXX: Some file systems stash data on stats objects.
this.fileData = null;
this.size = size;
var currentTime = 0;
if (typeof (atimeMs) !== 'number') {
currentTime = Date.now();
atimeMs = currentTime;
}
if (typeof (mtimeMs) !== 'number') {
if (!currentTime) {
currentTime = Date.now();
}
mtimeMs = currentTime;
}
if (typeof (ctimeMs) !== 'number') {
if (!currentTime) {
currentTime = Date.now();
}
ctimeMs = currentTime;
}
if (typeof (birthtimeMs) !== 'number') {
if (!currentTime) {
currentTime = Date.now();
}
birthtimeMs = currentTime;
}
this.atimeMs = atimeMs;
this.ctimeMs = ctimeMs;
this.mtimeMs = mtimeMs;
this.birthtimeMs = birthtimeMs;
if (!mode) {
switch (itemType) {
case FileType.FILE:
this.mode = 0x1a4;
break;
case FileType.DIRECTORY:
default:
this.mode = 0x1ff;
}
}
else {
this.mode = mode;
}
// number of 512B blocks allocated
this.blocks = Math.ceil(size / 512);
// Check if mode also includes top-most bits, which indicate the file's
// type.
if (this.mode < 0x1000) {
this.mode |= itemType;
}
};
var prototypeAccessors = { atime: { configurable: true },mtime: { configurable: true },ctime: { configurable: true },birthtime: { configurable: true } };
Stats.fromBuffer = function fromBuffer (buffer) {
var size = buffer.readUInt32LE(0), mode = buffer.readUInt32LE(4), atime = buffer.readDoubleLE(8), mtime = buffer.readDoubleLE(16), ctime = buffer.readDoubleLE(24);
return new Stats(mode & 0xF000, size, mode & 0xFFF, atime, mtime, ctime);
};
/**
* Clones the stats object.
*/
Stats.clone = function clone (s) {
return new Stats(s.mode & 0xF000, s.size, s.mode & 0xFFF, s.atimeMs, s.mtimeMs, s.ctimeMs, s.birthtimeMs);
};
prototypeAccessors.atime.get = function () {
return new Date(this.atimeMs);
};
prototypeAccessors.mtime.get = function () {
return new Date(this.mtimeMs);
};
prototypeAccessors.ctime.get = function () {
return new Date(this.ctimeMs);
};
prototypeAccessors.birthtime.get = function () {
return new Date(this.birthtimeMs);
};
Stats.prototype.toBuffer = function toBuffer () {
var buffer = Buffer.alloc(32);
buffer.writeUInt32LE(this.size, 0);
buffer.writeUInt32LE(this.mode, 4);
buffer.writeDoubleLE(this.atime.getTime(), 8);
buffer.writeDoubleLE(this.mtime.getTime(), 16);
buffer.writeDoubleLE(this.ctime.getTime(), 24);
return buffer;
};
/**
* @return [Boolean] True if this item is a file.
*/
Stats.prototype.isFile = function isFile () {
return (this.mode & 0xF000) === FileType.FILE;
};
/**
* @return [Boolean] True if this item is a directory.
*/
Stats.prototype.isDirectory = function isDirectory () {
return (this.mode & 0xF000) === FileType.DIRECTORY;
};
/**
* @return [Boolean] True if this item is a symbolic link (only valid through lstat)
*/
Stats.prototype.isSymbolicLink = function isSymbolicLink () {
return (this.mode & 0xF000) === FileType.SYMLINK;
};
/**
* Change the mode of the file. We use this helper function to prevent messing
* up the type of the file, which is encoded in mode.
*/
Stats.prototype.chmod = function chmod (mode) {
this.mode = (this.mode & 0xF000) | mode;
};
// We don't support the following types of files.
Stats.prototype.isSocket = function isSocket () {
return false;
};
Stats.prototype.isBlockDevice = function isBlockDevice () {
return false;
};
Stats.prototype.isCharacterDevice = function isCharacterDevice () {
return false;
};
Stats.prototype.isFIFO = function isFIFO () {
return false;
};
Object.defineProperties( Stats.prototype, prototypeAccessors );
/**
* @hidden
*/
var toExport = typeof (window) !== 'undefined' ? window : typeof (self) !== 'undefined' ? self : global;
/**
* @hidden
*/
var bfsSetImmediate;
if (typeof (setImmediate) !== "undefined") {
bfsSetImmediate = setImmediate;
}
else {
var gScope = toExport;
var timeouts = [];
var messageName = "zero-timeout-message";
var canUsePostMessage = function () {
if (typeof gScope.importScripts !== 'undefined' || !gScope.postMessage) {
return false;
}
var postMessageIsAsync = true;
var oldOnMessage = gScope.onmessage;
gScope.onmessage = function () {
postMessageIsAsync = false;
};
gScope.postMessage('', '*');
gScope.onmessage = oldOnMessage;
return postMessageIsAsync;
};
if (canUsePostMessage()) {
bfsSetImmediate = function (fn) {
var args = [], len = arguments.length - 1;
while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
timeouts.push({ fn: fn, args: args });
gScope.postMessage(messageName, "*");
};
var handleMessage = function (event) {
if (event.source === self && event.data === messageName) {
if (event.stopPropagation) {
event.stopPropagation();
}
else {
event.cancelBubble = true;
}
if (timeouts.length > 0) {
var ref = timeouts.shift();
var fn = ref.fn;
var args = ref.args;
return fn.apply(void 0, args);
}
}
};
if (gScope.addEventListener) {
gScope.addEventListener('message', handleMessage, true);
}
else {
gScope.attachEvent('onmessage', handleMessage);
}
}
else if (gScope.MessageChannel) {
// WebWorker MessageChannel
var channel = new gScope.MessageChannel();
channel.port1.onmessage = function (event) {
if (timeouts.length > 0) {
var ref = timeouts.shift();
var fn = ref.fn;
var args = ref.args;
return fn.apply(void 0, args);
}
};
bfsSetImmediate = function (fn) {
var args = [], len = arguments.length - 1;
while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
timeouts.push({ fn: fn, args: args });
channel.port2.postMessage('');
};
}
else {
bfsSetImmediate = function (fn) {
var args = [], len = arguments.length - 1;
while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
return setTimeout.apply(void 0, [ fn, 0 ].concat( args ));
};
}
}
var setImmediate$1 = bfsSetImmediate;
var EventEmitter = __webpack_require__(/*! events */ "./node_modules/events/events.js");
var FileWatcher = function FileWatcher() {
this.watchEntries = [];
};
FileWatcher.prototype.triggerWatch = function triggerWatch (filename, event, newStats) {
var this$1 = this;
var validEntries = this.watchEntries.filter(function (entry) {
if (entry.filename === filename) {
return true;
}
if (entry.recursive && filename.startsWith(entry.filename)) {
return true;
}
return false;
});
validEntries.forEach(function (entry) {
if (entry.callback) {
entry.callback(event, filename);
}
var newStatsArg = newStats || entry.curr;
var oldStatsArg = entry.curr || newStats;
if (newStatsArg && oldStatsArg && entry.fileCallback) {
entry.fileCallback(newStatsArg, oldStatsArg);
entry.curr = newStatsArg;
}
entry.watcher.emit(event);
if (!entry.persistent) {
this$1.removeEntry(entry);
}
});
};
FileWatcher.prototype.watch = function watch (filename, arg2, listener) {
var this$1 = this;
if ( listener === void 0 ) listener = (function () { });
var watcher = new EventEmitter();
var watchEntry = {
filename: filename,
watcher: watcher,
};
watcher.close = function () {
this$1.removeEntry(watchEntry);
};
if (typeof arg2 === 'object') {
watchEntry.recursive = arg2.recursive;
watchEntry.persistent = arg2.persistent === undefined ? true : arg2.persistent;
watchEntry.callback = listener;
}
else if (typeof arg2 === 'function') {
watchEntry.callback = arg2;
}
this.watchEntries.push(watchEntry);
return watchEntry.watcher;
};
FileWatcher.prototype.watchFile = function watchFile (curr, filename, arg2, listener) {
var this$1 = this;
if ( listener === void 0 ) listener = (function () { });
var watcher = new EventEmitter();
var watchEntry = {
filename: filename,
watcher: watcher,
curr: curr,
};
watcher.close = function () {
this$1.removeEntry(watchEntry);
};
if (typeof arg2 === 'object') {
watchEntry.recursive = arg2.recursive;
watchEntry.persistent = arg2.persistent === undefined ? true : arg2.persistent;
watchEntry.fileCallback = listener;
}
else if (typeof arg2 === 'function') {
watchEntry.fileCallback = arg2;
}
this.watchEntries.push(watchEntry);
return watchEntry.watcher;
};
FileWatcher.prototype.unwatchFile = function unwatchFile (filename, listener) {
this.watchEntries = this.watchEntries.filter(function (entry) { return entry.filename !== filename && entry.fileCallback !== listener; });
};
FileWatcher.prototype.removeEntry = function removeEntry (watchEntry) {
this.watchEntries = this.watchEntries.filter(function (en) { return en !== watchEntry; });
};
/** Used for unit testing. Defaults to a NOP. */
var wrapCbHook = function (cb, numArgs) {
return cb;
};
/**
* Wraps a callback function, ensuring it is invoked through setImmediate.
* @hidden
*/
function wrapCb(cb, numArgs) {
if (typeof cb !== 'function') {
throw new Error('Callback must be a function.');
}
var hookedCb = wrapCbHook(cb, numArgs);
// We could use `arguments`, but Function.call/apply is expensive. And we only
// need to handle 1-3 arguments
switch (numArgs) {
case 1:
return function (arg1) {
setImmediate$1(function () {
return hookedCb(arg1);
});
};
case 2:
return function (arg1, arg2) {
setImmediate$1(function () {
return hookedCb(arg1, arg2);
});
};
case 3:
return function (arg1, arg2, arg3) {
setImmediate$1(function () {
return hookedCb(arg1, arg2, arg3);
});
};
default:
throw new Error('Invalid invocation of wrapCb.');
}
}
/**
* @hidden
*/
function assertRoot(fs) {
if (fs) {
return fs;
}
throw new ApiError(ErrorCode.EIO, "Initialize BrowserFS with a file system using BrowserFS.initialize(filesystem)");
}
/**
* @hidden
*/
function normalizeMode(mode, def) {
switch (typeof mode) {
case 'number':
// (path, flag, mode, cb?)
return mode;
case 'string':
// (path, flag, modeString, cb?)
var trueMode = parseInt(mode, 8);
if (!isNaN(trueMode)) {
return trueMode;
}
// Invalid string.
return def;
default:
return def;
}
}
/**
* @hidden
*/
function normalizeTime(time) {
if (time instanceof Date) {
return time;
}
if (typeof time === 'number') {
return new Date(time * 1000);
}
throw new ApiError(ErrorCode.EINVAL, "Invalid time.");
}
/**
* @hidden
*/
function normalizePath(p) {
// Node doesn't allow null characters in paths.
if (p.indexOf('\u0000') >= 0) {
throw new ApiError(ErrorCode.EINVAL, 'Path must be a string without null bytes.');
}
else if (p === '') {
throw new ApiError(ErrorCode.EINVAL, 'Path must not be empty.');
}
return path.resolve(p);
}
/**
* @hidden
*/
function normalizeOptions(options, defEnc, defFlag, defMode) {
// typeof null === 'object' so special-case handing is needed.
switch (options === null ? 'null' : typeof options) {
case 'object':
return {
encoding: typeof options.encoding !== 'undefined' ? options.encoding : defEnc,
flag: typeof options.flag !== 'undefined' ? options.flag : defFlag,
mode: normalizeMode(options.mode, defMode)
};
case 'string':
return {
encoding: options,
flag: defFlag,
mode: defMode
};
case 'null':
case 'undefined':
case 'function':
return {
encoding: defEnc,
flag: defFlag,
mode: defMode
};
default:
throw new TypeError(("\"options\" must be a string or an object, got " + (typeof options) + " instead."));
}
}
/**
* The default callback is a NOP.
* @hidden
* @private
*/
function nopCb() {
// NOP.
}
/**
* The node frontend to all filesystems.
* This layer handles:
*
* * Sanity checking inputs.
* * Normalizing paths.
* * Resetting stack depth for asynchronous operations which may not go through
* the browser by wrapping all input callbacks using `setImmediate`.
* * Performing the requested operation through the filesystem or the file
* descriptor, as appropriate.
* * Handling optional arguments and setting default arguments.
* @see http://nodejs.org/api/fs.html
*/
var FS = function FS() {
this.root = null;
this.fdMap = {};
this.nextFd = 100;
this.fileWatcher = new FileWatcher();
};
FS.prototype.initialize = function initialize (rootFS) {
if (!rootFS.constructor.isAvailable()) {
throw new ApiError(ErrorCode.EINVAL, 'Tried to instantiate BrowserFS with an unavailable file system.');
}
return this.root = rootFS;
};
/**
* converts Date or number to a fractional UNIX timestamp
* Grabbed from NodeJS sources (lib/fs.js)
*/
FS.prototype._toUnixTimestamp = function _toUnixTimestamp (time) {
if (typeof time === 'number') {
return time;
}
if (time instanceof Date) {
return time.getTime() / 1000;
}
throw new Error("Cannot parse time: " + time);
};
/**
* **NONSTANDARD**: Grab the FileSystem instance that backs this API.
* @return [BrowserFS.FileSystem | null] Returns null if the file system has
* not been initialized.
*/
FS.prototype.getRootFS = function getRootFS () {
if (this.root) {
return this.root;
}
return null;
};
// FILE OR DIRECTORY METHODS
/**
* Asynchronous rename. No arguments other than a possible exception are given
* to the completion callback.
* @param oldPath
* @param newPath
* @param callback
*/
FS.prototype.rename = function rename (oldPath, newPath, cb) {
var this$1 = this;
if ( cb === void 0 ) cb = nopCb;
var newCb = wrapCb(cb, 1);
try {
setImmediate$1(function () {
this$1.fileWatcher.triggerWatch(oldPath, 'rename');
this$1.stat(newPath, function (err, stat) {
if (err) {
return;
}
this$1.fileWatcher.triggerWatch(newPath, 'rename', stat);
});
});
assertRoot(this.root).rename(normalizePath(oldPath), normalizePath(newPath), newCb);
}
catch (e) {
newCb(e);
}
};
/**
* Synchronous rename.
* @param oldPath
* @param newPath
*/
FS.prototype.renameSync = function renameSync (oldPath, newPath) {
var this$1 = this;
setImmediate$1(function () {
this$1.fileWatcher.triggerWatch(oldPath, 'rename');
this$1.fileWatcher.triggerWatch(newPath, 'rename');
});
assertRoot(this.root).renameSync(normalizePath(oldPath), normalizePath(newPath));
};
/**
* Test whether or not the given path exists by checking with the file system.
* Then call the callback argument with either true or false.
* @example Sample invocation
* fs.exists('/etc/passwd', function (exists) {
* util.debug(exists ? "it's there" : "no passwd!");
* });
* @param path
* @param callback
*/
FS.prototype.exists = function exists (path, cb) {
if ( cb === void 0 ) cb = nopCb;
var newCb = wrapCb(cb, 1);
try {
return assertRoot(this.root).exists(normalizePath(path), newCb);
}
catch (e) {
// Doesn't return an error. If something bad happens, we assume it just
// doesn't exist.
return newCb(false);
}
};
/**
* Test whether or not the given path exists by checking with the file system.
* @param path
* @return [boolean]
*/
FS.prototype.existsSync = function existsSync (path) {
try {
return assertRoot(this.root).existsSync(normalizePath(path));
}
catch (e) {
// Doesn't return an error. If something bad happens, we assume it just
// doesn't exist.
return false;
}
};
/**
* Asynchronous `stat`.
* @param path
* @param callback
*/
FS.prototype.stat = function stat (path, cb) {
if ( cb === void 0 ) cb = nopCb;
var newCb = wrapCb(cb, 2);
try {
return assertRoot(this.root).stat(normalizePath(path), false, newCb);
}
catch (e) {
return newCb(e);
}
};
/**
* Synchronous `stat`.
* @param path
* @return [BrowserFS.node.fs.Stats]
*/
FS.prototype.statSync = function statSync (path) {
return assertRoot(this.root).statSync(normalizePath(path), false);
};
/**
* Asynchronous `lstat`.
* `lstat()` is identical to `stat()`, except that if path is a symbolic link,
* then the link itself is stat-ed, not the file that it refers to.
* @param path
* @param callback
*/
FS.prototype.lstat = function lstat (path, cb) {
if ( cb === void 0 ) cb = nopCb;
var newCb = wrapCb(cb, 2);
try {
return assertRoot(this.root).stat(normalizePath(path), true, newCb);
}
catch (e) {
return newCb(e);
}
};
/**
* Synchronous `lstat`.
* `lstat()` is identical to `stat()`, except that if path is a symbolic link,
* then the link itself is stat-ed, not the file that it refers to.
* @param path
* @return [BrowserFS.node.fs.Stats]
*/
FS.prototype.lstatSync = function lstatSync (path) {
return assertRoot(this.root).statSync(normalizePath(path), true);
};
FS.prototype.truncate = function truncate (path, arg2, cb) {
var this$1 = this;
if ( arg2 === void 0 ) arg2 = 0;
if ( cb === void 0 ) cb = nopCb;
var len = 0;
if (typeof arg2 === 'function') {
cb = arg2;
}
else if (typeof arg2 === 'number') {
len = arg2;
}
var newCb = wrapCb(cb, 1);
try {
if (len < 0) {
throw new ApiError(ErrorCode.EINVAL);
}
setImmediate$1(function () {
this$1.stat(path, function (err, stat) {
this$1.fileWatcher.triggerWatch(path, 'change', stat);
});
});
return assertRoot(this.root).truncate(normalizePath(path), len, newCb);
}
catch (e) {
return newCb(e);
}
};
/**
* Synchronous `truncate`.
* @param path
* @param len
*/
FS.prototype.truncateSync = function truncateSync (path, len) {
var this$1 = this;
if ( len === void 0 ) len = 0;
if (len < 0) {
throw new ApiError(ErrorCode.EINVAL);
}
setImmediate$1(function () {
this$1.stat(path, function (err, stat) {
this$1.fileWatcher.triggerWatch(path, 'change', stat);
});
});
return assertRoot(this.root).truncateSync(normalizePath(path), len);
};
/**
* Asynchronous `unlink`.
* @param path
* @param callback
*/
FS.prototype.unlink = function unlink (path, cb) {
var this$1 = this;
if ( cb === void 0 ) cb = nopCb;
var newCb = wrapCb(cb, 1);
try {
setImmediate$1(function () {
this$1.fileWatcher.triggerWatch(path, 'rename', new Stats(FileType.FILE, 0, undefined, 0, 0, 0, 0));
});
return assertRoot(this.root).unlink(normalizePath(path), newCb);
}
catch (e) {
return newCb(e);
}
};
/**
* Synchronous `unlink`.
* @param path
*/
FS.prototype.unlinkSync = function unlinkSync (path) {
var this$1 = this;
setImmediate$1(function () {
this$1.fileWatcher.triggerWatch(path, 'rename', new Stats(FileType.FILE, 0, undefined, 0, 0, 0, 0));
});
return assertRoot(this.root).unlinkSync(normalizePath(path));
};
FS.prototype.open = function open (path, flag, arg2, cb) {
var this$1 = this;
if ( cb === void 0 ) cb = nopCb;
var mode = normalizeMode(arg2, 0x1a4);
cb = typeof arg2 === 'function' ? arg2 : cb;
var newCb = wrapCb(cb, 2);
try {
assertRoot(this.root).open(normalizePath(path), FileFlag.getFileFlag(flag), mode, function (e, file) {
if (file) {
newCb(e, this$1.getFdForFile(file));
}
else {
newCb(e);
}
});
}
catch (e) {
newCb(e);
}
};
/**
* Synchronous file open.
* @see http://www.manpagez.com/man/2/open/
* @param path
* @param flags
* @param mode defaults to `0644`
* @return [BrowserFS.File]
*/
FS.prototype.openSync = function openSync (path, flag, mode) {
if ( mode === void 0 ) mode = 0x1a4;
return this.getFdForFile(assertRoot(this.root).openSync(normalizePath(path), FileFlag.getFileFlag(flag), normalizeMode(mode, 0x1a4)));
};
FS.prototype.readFile = function readFile (filename, arg2, cb) {
if ( arg2 === void 0 ) arg2 = {};
if ( cb === void 0 ) cb = nopCb;
var options = normalizeOptions(arg2, null, 'r', null);
cb = typeof arg2 === 'function' ? arg2 : cb;
var newCb = wrapCb(cb, 2);
try {
var flag = FileFlag.getFileFlag(options.flag);
if (!flag.isReadable()) {
return newCb(new ApiError(ErrorCode.EINVAL, 'Flag passed to readFile must allow for reading.'));
}
return assertRoot(this.root).readFile(normalizePath(filename), options.encoding, flag, newCb);
}
catch (e) {
return newCb(e);
}
};
FS.prototype.readFileSync = function readFileSync (filename, arg2) {
if ( arg2 === void 0 ) arg2 = {};
var options = normalizeOptions(arg2, null, 'r', null);
var flag = FileFlag.getFileFlag(options.flag);
if (!flag.isReadable()) {
throw new ApiError(ErrorCode.EINVAL, 'Flag passed to readFile must allow for reading.');
}
return assertRoot(this.root).readFileSync(normalizePath(filename), options.encoding, flag);
};
FS.prototype.writeFile = function writeFile (filename, data, arg3, cb) {
var this$1 = this;
if ( arg3 === void 0 ) arg3 = {};
if ( cb === void 0 ) cb = nopCb;
var options = normalizeOptions(arg3, 'utf8', 'w', 0x1a4);
cb = typeof arg3 === 'function' ? arg3 : cb;
var newCb = wrapCb(cb, 1);
try {
var flag = FileFlag.getFileFlag(options.flag);
if (!flag.isWriteable()) {
return newCb(new ApiError(ErrorCode.EINVAL, 'Flag passed to writeFile must allow for writing.'));
}
assertRoot(this.root).writeFile(normalizePath(filename), data, options.encoding, flag, options.mode, function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
setImmediate$1(function () {
this$1.stat(filename, function (_err, stat) {
this$1.fileWatcher.triggerWatch(filename, 'change', stat);
});
});
newCb.apply(void 0, args);
});
}
catch (e) {
return newCb(e);
}
};
FS.prototype.writeFileSync = function writeFileSync (filename, data, arg3) {
var this$1 = this;
var options = normalizeOptions(arg3, 'utf8', 'w', 0x1a4);
var flag = FileFlag.getFileFlag(options.flag);
if (!flag.isWriteable()) {
throw new ApiError(ErrorCode.EINVAL, 'Flag passed to writeFile must allow for writing.');
}
setImmediate$1(function () {
this$1.stat(filename, function (err, stat) {
this$1.fileWatcher.triggerWatch(filename, 'change', stat);
});
});
return assertRoot(this.root).writeFileSync(normalizePath(filename), data, options.encoding, flag, options.mode);
};
FS.prototype.appendFile = function appendFile (filename, data, arg3, cb) {
var this$1 = this;
if ( cb === void 0 ) cb = nopCb;
var options = normalizeOptions(arg3, 'utf8', 'a', 0x1a4);
cb = typeof arg3 === 'function' ? arg3 : cb;
var newCb = wrapCb(cb, 1);
try {
var flag = FileFlag.getFileFlag(options.flag);
if (!flag.isAppendable()) {
return newCb(new ApiError(ErrorCode.EINVAL, 'Flag passed to appendFile must allow for appending.'));
}
setImmediate$1(function () {
this$1.stat(filename, function (err, stat) {
this$1.fileWatcher.triggerWatch(filename, 'rename', stat);
});
});
assertRoot(this.root).appendFile(normalizePath(filename), data, options.encoding, flag, options.mode, newCb);
}
catch (e) {
newCb(e);
}
};
FS.prototype.appendFileSync = function appendFileSync (filename, data, arg3) {
var this$1 = this;
var options = normalizeOptions(arg3, 'utf8', 'a', 0x1a4);
var flag = FileFlag.getFileFlag(options.flag);
if (!flag.isAppendable()) {
throw new ApiError(ErrorCode.EINVAL, 'Flag passed to appendFile must allow for appending.');
}
setImmediate$1(function () {
this$1.stat(filename, function (err, stat) {
this$1.fileWatcher.triggerWatch(filename, 'change', stat);
});
});
return assertRoot(this.root).appendFileSync(normalizePath(filename), data, options.encoding, flag, options.mode);
};
// FILE DESCRIPTOR METHODS
/**
* Asynchronous `fstat`.
* `fstat()` is identical to `stat()`, except that the file to be stat-ed is
* specified by the file descriptor `fd`.
* @param fd
* @param callback
*/
FS.prototype.fstat = function fstat (fd, cb) {
if ( cb === void 0 ) cb = nopCb;
var newCb = wrapCb(cb, 2);
try {
var file = this.fd2file(fd);
file.stat(newCb);
}
catch (e) {
newCb(e);
}
};
/**
* Synchronous `fstat`.
* `fstat()` is identical to `stat()`, except that the file to be stat-ed is
* specified by the file descriptor `fd`.
* @param fd
* @return [BrowserFS.node.fs.Stats]
*/
FS.prototype.fstatSync = function fstatSync (fd) {
return this.fd2file(fd).statSync();
};
/**
* Asynchronous close.
* @param fd
* @param callback
*/
FS.prototype.close = function close (fd, cb) {
var this$1 = this;
if ( cb === void 0 ) cb = nopCb;
var newCb = wrapCb(cb, 1);
try {
this.fd2file(fd).close(function (e) {
if (!e) {
this$1.closeFd(fd);
}
newCb(e);
});
}
catch (e) {
newCb(e);
}
};
/**
* Synchronous close.
* @param fd
*/
FS.prototype.closeSync = function closeSync (fd) {
this.fd2file(fd).closeSync();
this.closeFd(fd);
};
FS.prototype.ftruncate = function ftruncate (fd, arg2, cb) {
if ( cb === void 0 ) cb = nopCb;
var length = typeof arg2 === 'number' ? arg2 : 0;
cb = typeof arg2 === 'function' ? arg2 : cb;
var newCb = wrapCb(cb, 1);
try {
var file = this.fd2file(fd);
if (length < 0) {
throw new ApiError(ErrorCode.EINVAL);
}
file.truncate(length, newCb);
}
catch (e) {
newCb(e);
}
};
/**
* Synchronous ftruncate.
* @param fd
* @param len
*/
FS.prototype.ftruncateSync = function ftruncateSync (fd, len) {
if ( len === void 0 ) len = 0;
var file = this.fd2file(fd);
if (len < 0) {
throw new ApiError(ErrorCode.EINVAL);
}
file.truncateSync(len);
};
/**
* Asynchronous fsync.
* @param fd
* @param callback
*/
FS.prototype.fsync = function fsync (fd, cb) {
if ( cb === void 0 ) cb = nopCb;
var newCb = wrapCb(cb, 1);
try {
this.fd2file(fd).sync(newCb);
}
catch (e) {
newCb(e);
}
};
/**
* Synchronous fsync.
* @param fd
*/
FS.prototype.fsyncSync = function fsyncSync (fd) {
this.fd2file(fd).syncSync();
};
/**
* Asynchronous fdatasync.
* @param fd
* @param callback
*/
FS.prototype.fdatasync = function fdatasync (fd, cb) {
if ( cb === void 0 ) cb = nopCb;
var newCb = wrapCb(cb, 1);
try {
this.fd2file(fd).datasync(newCb);
}
catch (e) {
newCb(e);
}
};
/**
* Synchronous fdatasync.
* @param fd
*/
FS.prototype.fdatasyncSync = function fdatasyncSync (fd) {
this.fd2file(fd).datasyncSync();
};
FS.prototype.write = function write (fd, arg2, arg3, arg4, arg5, cb) {
if ( cb === void 0 ) cb = nopCb;
var buffer;
var offset;
var length;
var position = null;
if (typeof arg2 === 'string') {
// Signature 1: (fd, string, [position?, [encoding?]], cb?)
var encoding = 'utf8';
switch (typeof arg3) {
case 'function':
// (fd, string, cb)
cb = arg3;
break;
case 'number':
// (fd, string, position, encoding?, cb?)
position = arg3;
encoding = typeof arg4 === 'string' ? arg4 : 'utf8';
cb = typeof arg5 === 'function' ? arg5 : cb;
break;
default:
// ...try to find the callback and get out of here!
cb = typeof arg4 === 'function' ? arg4 : typeof arg5 === 'function' ? arg5 : cb;
return cb(new ApiError(ErrorCode.EINVAL, 'Invalid arguments.'));
}
buffer = Buffer.from(arg2, encoding);
offset = 0;
length = buffer.length;
}
else {
// Signature 2: (fd, buffer, offset, length, position?, cb?)
buffer = arg2;
offset = arg3;
length = arg4;
position = typeof arg5 === 'number' ? arg5 : null;
cb = typeof arg5 === 'function' ? arg5 : cb;
}
var newCb = wrapCb(cb, 3);
try {
var file = this.fd2file(fd);
if (position === undefined || position === null) {
position = file.getPos();
}
file.write(buffer, offset, length, position, newCb);
}
catch (e) {
newCb(e);
}
};
FS.prototype.writeSync = function writeSync (fd, arg2, arg3, arg4, arg5) {
var buffer;
var offset = 0;
var length;
var position;
if (typeof arg2 === 'string') {
// Signature 1: (fd, string, [position?, [encoding?]])
position = typeof arg3 === 'number' ? arg3 : null;
var encoding = typeof arg4 === 'string' ? arg4 : 'utf8';
offset = 0;
buffer = Buffer.from(arg2, encoding);
length = buffer.length;
}
else {
// Signature 2: (fd, buffer, offset, length, position?)
buffer = arg2;
offset = arg3;
length = arg4;
position = typeof arg5 === 'number' ? arg5 : null;
}
var file = this.fd2file(fd);
if (position === undefined || position === null) {
position = file.getPos();
}
return file.writeSync(buffer, offset, length, position);
};
FS.prototype.read = function read (fd, arg2, arg3, arg4, arg5, cb) {
if ( cb === void 0 ) cb = nopCb;
var position;
var offset;
var length;
var buffer;
var newCb;
if (typeof arg2 === 'number') {
// legacy interface
// (fd, length, position, encoding, callback)
length = arg2;
position = arg3;
var encoding = arg4;
cb = typeof arg5 === 'function' ? arg5 : cb;
offset = 0;
buffer = Buffer.alloc(length);
// XXX: Inefficient.
// Wrap the cb so we shelter upper layers of the API from these
// shenanigans.
newCb = wrapCb(function (err, bytesRead, buf) {
if (err) {
return cb(err);
}
cb(err, buf.toString(encoding), bytesRead);
}, 3);
}
else {
buffer = arg2;
offset = arg3;
length = arg4;
position = arg5;
newCb = wrapCb(cb, 3);
}
try {
var file = this.fd2file(fd);
if (position === undefined || position === null) {
position = file.getPos();
}
file.read(buffer, offset, length, position, newCb);
}
catch (e) {
newCb(e);
}
};
FS.prototype.readSync = function readSync (fd, arg2, arg3, arg4, arg5) {
var shenanigans = false;
var buffer;
var offset;
var length;
var position;
var encoding = 'utf8';
if (typeof arg2 === 'number') {
length = arg2;
position = arg3;
encoding = arg4;
offset = 0;
buffer = Buffer.alloc(length);
shenanigans = true;
}
else {
buffer = arg2;
offset = arg3;
length = arg4;
position = arg5;
}
var file = this.fd2file(fd);
if (position === undefined || position === null) {
position = file.getPos();
}
var rv = file.readSync(buffer, offset, length, position);
if (!shenanigans) {
return rv;
}
return [buffer.toString(encoding), rv];
};
/**
* Asynchronous `fchown`.
* @param fd
* @param uid
* @param gid
* @param callback
*/
FS.prototype.fchown = function fchown (fd, uid, gid, callback) {
if ( callback === void 0 ) callback = nopCb;
var newCb = wrapCb(callback, 1);
try {
this.fd2file(fd).chown(uid, gid, newCb);
}
catch (e) {
newCb(e);
}
};
/**
* Synchronous `fchown`.
* @param fd
* @param uid
* @param gid
*/
FS.prototype.fchownSync = function fchownSync (fd, uid, gid) {
this.fd2file(fd).chownSync(uid, gid);
};
/**
* Asynchronous `fchmod`.
* @param fd
* @param mode
*