UNPKG

@verdaccio/local-storage

Version:

Local storage implementation

273 lines (272 loc) 8.6 kB
const require_runtime = require("./_virtual/_rolldown/runtime.js"); let debug = require("debug"); debug = require_runtime.__toESM(debug); let fs = require("fs"); fs = require_runtime.__toESM(fs); let lodash = require("lodash"); lodash = require_runtime.__toESM(lodash); let mkdirp = require("mkdirp"); mkdirp = require_runtime.__toESM(mkdirp); let path = require("path"); path = require_runtime.__toESM(path); let _verdaccio_core = require("@verdaccio/core"); let _verdaccio_file_locking = require("@verdaccio/file-locking"); let _verdaccio_streams = require("@verdaccio/streams"); //#region src/local-fs.ts var fileExist = "EEXISTS"; var noSuchFile = "ENOENT"; var pkgFileName = "package.json"; var debug$1 = (0, debug.default)("verdaccio:plugin:local-storage:fs"); var fSError = function(message, code = 409) { const err = _verdaccio_core.errorUtils.getCode(code, message); err.code = message; return err; }; var tempFile = function(str) { return `${str}.tmp${String(Math.random()).substr(2)}`; }; var renameTmp = function(src, dst, _cb) { const cb = (err) => { if (err) fs.default.unlink(src, () => {}); _cb(err); }; if (process.platform !== "win32") return fs.default.rename(src, dst, cb); const tmp = tempFile(dst); fs.default.rename(dst, tmp, function(err) { fs.default.rename(src, dst, cb); if (!err) fs.default.unlink(tmp, () => {}); }); }; var LocalFS = class { path; logger; constructor(path$2, logger) { this.path = path$2; this.logger = logger; } /** * This function allows to update the package thread-safely Algorithm: 1. lock package.json for writing 2. read package.json 3. updateFn(pkg, cb), and wait for cb 4. write package.json.tmp 5. move package.json.tmp package.json 6. callback(err?) * @param {*} name * @param {*} updateHandler * @param {*} onWrite * @param {*} transformPackage * @param {*} onEnd */ updatePackage(name, updateHandler, onWrite, transformPackage, onEnd) { this._lockAndReadJSON(pkgFileName, (err, json) => { let locked = false; const self = this; const unLockCallback = function(lockError) { const _args = arguments; if (locked) self._unlockJSON(pkgFileName, () => { if (lockError !== null) debug$1("lock file: %o has failed with error %o", name, lockError); onEnd.apply(lockError, _args); }); else { debug$1("file: %o has been updated", name); onEnd(..._args); } }; if (!err) { locked = true; debug$1("file: %o has been locked", name); } if (lodash.default.isNil(err) === false) if (err.code === "EAGAIN") return unLockCallback(_verdaccio_core.errorUtils.getInternalError("resource temporarily unavailable")); else if (err.code === "ENOENT") return unLockCallback(_verdaccio_core.errorUtils.getNotFound()); else return unLockCallback(err); updateHandler(json, (err) => { if (err) return unLockCallback(err); onWrite(name, transformPackage(json), unLockCallback); }); }); } deletePackage(packageName, callback) { debug$1("delete a package %o", packageName); return fs.default.unlink(this._getStorage(packageName), callback); } removePackage(callback) { debug$1("remove a package %o", this.path); fs.default.rmdir(this._getStorage("."), callback); } createPackage(name, value, cb) { debug$1("create a package %o", name); this._createFile(this._getStorage(pkgFileName), this._convertToString(value), cb); } savePackage(name, value, cb) { debug$1("save a package %o", name); this._writeFile(this._getStorage(pkgFileName), this._convertToString(value), cb); } readPackage(name, cb) { debug$1("read a package %o", name); this._readStorageFile(this._getStorage(pkgFileName)).then((res) => { try { const data = JSON.parse(res.toString("utf8")); debug$1("read storage file %o has succeed", name); cb(null, data); } catch (err) { debug$1("parse storage file %o has failed with error %o", name, err); cb(err); } }, (err) => { debug$1("read storage file %o has failed with error %o", name, err); return cb(err); }); } writeTarball(name) { const uploadStream = new _verdaccio_streams.UploadTarball({}); debug$1("write a tarball for a package %o", name); let _ended = 0; uploadStream.on("end", function() { _ended = 1; }); const pathName = this._getStorage(name); fs.default.access(pathName, (fileNotFound) => { if (!fileNotFound) uploadStream.emit("error", fSError(fileExist)); else { const temporalName = path.default.join(this.path, `${name}.tmp-${String(Math.random()).replace(/^0\./, "")}`); debug$1("write a temporal name %o", temporalName); const file = fs.default.createWriteStream(temporalName); const removeTempFile = () => fs.default.unlink(temporalName, () => {}); let opened = false; uploadStream.pipe(file); uploadStream.done = function() { const onend = function() { file.on("close", function() { renameTmp(temporalName, pathName, function(err) { if (err) uploadStream.emit("error", err); else uploadStream.emit("success"); }); }); file.end(); }; if (_ended) onend(); else uploadStream.on("end", onend); }; uploadStream.abort = function() { if (opened) { opened = false; file.on("close", function() { removeTempFile(); }); } else removeTempFile(); file.end(); }; file.on("open", function() { opened = true; uploadStream.emit("open"); }); file.on("error", function(err) { uploadStream.emit("error", err); }); } }); return uploadStream; } readTarball(name) { const pathName = this._getStorage(name); debug$1("read a a tarball %o on path %o", name, pathName); const readTarballStream = new _verdaccio_streams.ReadTarball({}); const readStream = fs.default.createReadStream(pathName); readStream.on("error", function(err) { debug$1("error on read a tarball %o with error %o", name, err); readTarballStream.emit("error", err); }); readStream.on("open", function(fd) { fs.default.fstat(fd, function(err, stats) { if (lodash.default.isNil(err) === false) { debug$1("error on read a tarball %o with error %o", name, err); return readTarballStream.emit("error", err); } readTarballStream.emit("content-length", stats.size); readTarballStream.emit("open"); debug$1("open on read a tarball %o", name); readStream.pipe(readTarballStream); }); }); readTarballStream.abort = function() { debug$1("abort on read a tarball %o", name); readStream.close(); }; return readTarballStream; } _createFile(name, contents, callback) { debug$1(" create a new file: %o", name); fs.default.open(name, "wx", (err) => { if (err) { if (err.code === "EEXIST") { debug$1("file %o cannot be created, it already exists: %o", name); return callback(fSError(fileExist)); } } this._writeFile(name, contents, callback); }); } _readStorageFile(name) { return new Promise((resolve, reject) => { debug$1("reading the file: %o", name); fs.default.readFile(name, (err, data) => { if (err) { debug$1("error reading the file: %o with error %o", name, err); reject(err); } else { debug$1("read file %o succeed", name); resolve(data); } }); }); } _convertToString(value) { return JSON.stringify(value, null, " "); } _getStorage(fileName = "") { return path.default.join(this.path, fileName); } _writeFile(dest, data, cb) { const createTempFile = (cb) => { const tempFilePath = tempFile(dest); fs.default.writeFile(tempFilePath, data, (err) => { if (err) { debug$1("error on write the file: %o", dest); return cb(err); } debug$1("creating a new file:: %o", dest); renameTmp(tempFilePath, dest, cb); }); }; createTempFile((err) => { if (err && err.code === "ENOENT") (0, mkdirp.default)(path.default.dirname(dest)).then(() => { createTempFile(cb); }).catch((err) => { return cb(err); }); else cb(err); }); } _lockAndReadJSON(name, cb) { (0, _verdaccio_file_locking.readFile)(this._getStorage(name), { lock: true, parse: true }, (err, res) => { if (err) { debug$1("error on lock and read json for file: %o", name); return cb(err); } debug$1("lock and read json for file: %o", name); return cb(null, res); }); } _unlockJSON(name, cb) { (0, _verdaccio_file_locking.unlockFile)(this._getStorage(name), cb); } }; //#endregion exports.default = LocalFS; exports.noSuchFile = noSuchFile; //# sourceMappingURL=local-fs.js.map