@verdaccio/local-storage
Version:
Local storage implementation
273 lines (272 loc) • 8.6 kB
JavaScript
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