@shockpkg/core
Version:
shockpkg core
2,186 lines (1,729 loc) • 47.7 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Manager = void 0;
var _initializerDefineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/initializerDefineProperty"));
var _applyDecoratedDescriptor2 = _interopRequireDefault(require("@babel/runtime/helpers/applyDecoratedDescriptor"));
var _initializerWarningHelper2 = _interopRequireDefault(require("@babel/runtime/helpers/initializerWarningHelper"));
var _fsExtra = _interopRequireDefault(require("fs-extra"));
var _path = require("path");
var _constants = require("./constants");
var _decorators = require("./decorators");
var _dispatcher = require("./dispatcher");
var _lock = require("./lock");
var _packages = require("./packages");
var _request = require("./request");
var _util = require("./util");
var _zip = require("./zip");
var _dec, _dec2, _dec3, _dec4, _dec5, _dec6, _dec7, _dec8, _dec9, _dec10, _dec11, _dec12, _dec13, _dec14, _dec15, _class, _descriptor, _descriptor2, _descriptor3, _descriptor4, _descriptor5, _descriptor6, _descriptor7, _descriptor8, _descriptor9, _descriptor10, _descriptor11, _descriptor12, _descriptor13, _descriptor14, _descriptor15, _temp;
/**
* Manager constructor.
*
* @param path The path, defaults to environment variable or relative.
*/
let Manager = (_dec = (0, _decorators.property)(false), _dec2 = (0, _decorators.property)(false), _dec3 = (0, _decorators.property)(false), _dec4 = (0, _decorators.property)(false), _dec5 = (0, _decorators.property)(false), _dec6 = (0, _decorators.property)(false), _dec7 = (0, _decorators.property)(false), _dec8 = (0, _decorators.property)(false), _dec9 = (0, _decorators.property)(false), _dec10 = (0, _decorators.property)(false), _dec11 = (0, _decorators.property)(false), _dec12 = (0, _decorators.property)(false), _dec13 = (0, _decorators.property)(false), _dec14 = (0, _decorators.property)(false), _dec15 = (0, _decorators.property)(false), (_class = (_temp = class Manager extends Object {
/**
* Package install before events.
*/
/**
* Package install after events.
*/
/**
* Package install current events.
*/
/**
* Package download before events.
*/
/**
* Package download after events.
*/
/**
* Package download progress events.
*/
/**
* Package stream before events.
*/
/**
* Package stream after events.
*/
/**
* Package stream progress events.
*/
/**
* Package extract before events.
*/
/**
* Package extract after events.
*/
/**
* Package extract progress events.
*/
/**
* Package cleanup before events.
*/
/**
* Package cleanup after events.
*/
/**
* Packages URL.
*/
/**
* Packages file.
*/
/**
* Package file.
*/
/**
* Main directory.
*/
/**
* Meta directory.
*/
/**
* Temp directory.
*/
/**
* Path environment variable name.
*/
/**
* Packages URL environment variable name.
*/
/**
* Inited flag.
*/
/**
* Destroyed flag.
*/
/**
* Exclusive access flag.
*/
/**
* Root path.
*/
/**
* Lock file instance.
*/
/**
* Packages instance.
*/
/**
* Request instance.
*/
constructor(path = null) {
super();
this.eventPackageInstallBefore = new _dispatcher.Dispatcher(this);
this.eventPackageInstallAfter = new _dispatcher.Dispatcher(this);
this.eventPackageInstallCurrent = new _dispatcher.Dispatcher(this);
this.eventPackageDownloadBefore = new _dispatcher.Dispatcher(this);
this.eventPackageDownloadAfter = new _dispatcher.Dispatcher(this);
this.eventPackageDownloadProgress = new _dispatcher.Dispatcher(this);
this.eventPackageStreamBefore = new _dispatcher.Dispatcher(this);
this.eventPackageStreamAfter = new _dispatcher.Dispatcher(this);
this.eventPackageStreamProgress = new _dispatcher.Dispatcher(this);
this.eventPackageExtractBefore = new _dispatcher.Dispatcher(this);
this.eventPackageExtractAfter = new _dispatcher.Dispatcher(this);
this.eventPackageExtractProgress = new _dispatcher.Dispatcher(this);
this.eventPackageCleanupBefore = new _dispatcher.Dispatcher(this);
this.eventPackageCleanupAfter = new _dispatcher.Dispatcher(this);
(0, _initializerDefineProperty2.default)(this, "_packagesUrl", _descriptor, this);
(0, _initializerDefineProperty2.default)(this, "_packagesFile", _descriptor2, this);
(0, _initializerDefineProperty2.default)(this, "_packageFile", _descriptor3, this);
(0, _initializerDefineProperty2.default)(this, "_mainDir", _descriptor4, this);
(0, _initializerDefineProperty2.default)(this, "_metaDir", _descriptor5, this);
(0, _initializerDefineProperty2.default)(this, "_tempDir", _descriptor6, this);
(0, _initializerDefineProperty2.default)(this, "_pathEnv", _descriptor7, this);
(0, _initializerDefineProperty2.default)(this, "_packagesUrlEnv", _descriptor8, this);
(0, _initializerDefineProperty2.default)(this, "_inited", _descriptor9, this);
(0, _initializerDefineProperty2.default)(this, "_destroyed", _descriptor10, this);
(0, _initializerDefineProperty2.default)(this, "_exclusive", _descriptor11, this);
(0, _initializerDefineProperty2.default)(this, "_path", _descriptor12, this);
(0, _initializerDefineProperty2.default)(this, "_lock", _descriptor13, this);
(0, _initializerDefineProperty2.default)(this, "_packages", _descriptor14, this);
(0, _initializerDefineProperty2.default)(this, "_request", _descriptor15, this);
this._path = this._createPath(path);
const lock = this._createLock();
lock.eventCompromised.on(() => {// Do nothing, instead fail on next assert call.
});
this._lock = lock;
this._packagesUrl = this._createPackagesUrl(this._packagesUrl);
this._packages = this._createPackages();
this._request = this._createRequest();
}
/**
* Root path.
*/
get path() {
return this._path;
}
/**
* Packages URL.
*/
get packagesUrl() {
return this._packagesUrl;
}
/**
* Packages file.
*/
get packagesFile() {
return this._packagesFile;
}
/**
* Package file.
*/
get packageFile() {
return this._packageFile;
}
/**
* Packages file path.
*/
get pathMetaPackages() {
return this.pathToMeta(this.packagesFile);
}
/**
* Meta directory.
*/
get metaDir() {
return this._metaDir;
}
/**
* Temp directory.
*/
get tempDir() {
return this._tempDir;
}
/**
* Meta directory path for root path.
*/
get pathMeta() {
return this.pathToMeta();
}
/**
* Instance inited and not yet destroyed.
*/
get active() {
return !this._destroyed && this._inited;
}
/**
* Packages loaded.
*/
get loaded() {
return this._packages.loaded;
}
/**
* The lock file compromised.
*/
get lockCompromised() {
return this._lock.compromised;
}
/**
* Assert instance not inited.
*/
assertNotInited() {
this._exclusiveSync(() => {
this._assertNotInited();
});
}
/**
* Assert instance is active.
* Implies inited, not-destroyed, and lock-not-compromised assertions.
*/
assertActive() {
this._exclusiveSync(() => {
this._assertActive();
});
}
/**
* Assert instance all loaded, including the packages list.
* Implies all active assertions.
*/
assertLoaded() {
this._exclusiveSync(() => {
this._assertLoaded();
});
}
/**
* Initialize instance.
*/
async init() {
await this._exclusiveAsync(() => this._init());
}
/**
* Destroy instance.
*/
async destroy() {
await this._exclusiveAsync(() => this._destroy());
}
/**
* Run asyncronous function with automatic init and destroy.
*
* @param func Async function.
* @return Return value of the async function.
*/
async with(func) {
await this.init();
let r;
try {
r = await func.call(this, this);
} finally {
await this.destroy();
}
return r;
}
/**
* Itterate over the packages.
*/
packageItter() {
return this._exclusiveSync(() => this._packageItter());
}
/**
* Get package by the unique name.
*
* @param name Package name.
* @return The package or null.
*/
packageByName(name) {
return this._exclusiveSync(() => this._packageByName(name));
}
/**
* Get package by the sha256 hash.
*
* @param sha256 Package sha256.
* @return The package or null.
*/
packageBySha256(sha256) {
return this._exclusiveSync(() => this._packageBySha256(sha256));
}
/**
* Get package by the unique value.
*
* @param name Package unique.
* @return The package or null.
*/
packageByUnique(unique) {
return this._exclusiveSync(() => this._packageByUnique(unique));
}
/**
* Check if package is in packages collection.
*
* @param pkg Package instance.
* @return If the package instance is present.
*/
packageIsMember(pkg) {
return this._exclusiveSync(() => this._packageIsMember(pkg));
}
/**
* Read package install receipt.
*
* @param pkg The package.
* @return Install receipt.
*/
async packageInstallReceipt(pkg) {
return this._exclusiveAsync(() => this._packageMetaReceiptRead(pkg));
}
/**
* Get package install file.
*
* @param pkg The package.
* @return Path to install file.
*/
async packageInstallFile(pkg) {
return this._exclusiveAsync(() => this._packageInstallFile(pkg));
}
/**
* Verify package install file, using size and hash.
*
* @param pkg The package.
*/
async packageInstallVerify(pkg) {
await this._exclusiveAsync(() => this._packageInstallVerify(pkg));
}
/**
* Packages ordered by dependencies.
*
* @param pkgs Packages list.
* @return Packages list, sorted order.
*/
packagesDependOrdered(pkgs) {
return this._exclusiveSync(() => this._packagesDependOrdered(pkgs));
}
/**
* Update the package manager installed data.
* Updates the packages list.
*
* @return Update report.
*/
async update() {
return this._exclusiveAsync(() => this._update());
}
/**
* Check if a package is installed.
*
* @param pkg The package.
* @return True if already installed, else false.
*/
async isInstalled(pkg) {
return this._exclusiveAsync(() => this._isInstalled(pkg));
}
/**
* Check if a package is installed and up-to-date.
*
* @param pkg The package.
* @return True if already up-to-date, else false.
*/
async isCurrent(pkg) {
return this._exclusiveAsync(() => this._isCurrent(pkg));
}
/**
* List all installed packages.
*
* @return A list of installed package objects.
*/
async installed() {
return this._exclusiveAsync(() => this._installed());
}
/**
* List all outdated packages.
*
* @return The list of outdated package objects.
*/
async outdated() {
return this._exclusiveAsync(() => this._outdated());
}
/**
* An alias for upgradeSlim.
*
* @return List of packages upgraded.
*/
async upgrade() {
return this._exclusiveAsync(() => this._upgrade());
}
/**
* Upgrade any outdated packages.
*
* @return List of packages upgraded.
*/
async upgradeFull() {
return this._exclusiveAsync(() => this._upgradeFull());
}
/**
* Upgrade any outdated packages, using slim install method.
*
* @return List of packages upgraded.
*/
async upgradeSlim() {
return this._exclusiveAsync(() => this._upgradeSlim());
}
/**
* An alias for installSlim.
*
* @param pkg The package.
* @return True if was installed, false if already installed.
*/
async install(pkg) {
return this._exclusiveAsync(() => this._install(pkg));
}
/**
* Install package, with parents.
* Returns the list of packages installed to install.
* Returns empty array if current version is already installed.
*
* @param pkg The package.
* @return List of packages processed.
*/
async installFull(pkg) {
return this._exclusiveAsync(() => this._installFull(pkg));
}
/**
* Install multiple package with parents, higher dependencies first.
*
* @param pkgs Packages list.
* @return Installed list.
*/
async installFullMulti(pkgs) {
return this._exclusiveAsync(() => this._installFullMulti(pkgs));
}
/**
* Install package, without parents.
* Returns the list of packages downloaded or extracted to install.
* Returns empty array if current version is already installed.
*
* @param pkg The package.
* @return List of packages processed.
*/
async installSlim(pkg) {
return this._exclusiveAsync(() => this._installSlim(pkg));
}
/**
* Install multiple package without parents, higher dependencies first.
*
* @param pkgs Packages list.
* @return Installed list.
*/
async installSlimMulti(pkgs) {
return this._exclusiveAsync(() => this._installSlimMulti(pkgs));
}
/**
* Remove package.
*
* @param pkg The package.
* @return True if removed, false if nothing to remove.
*/
async remove(pkg) {
return this._exclusiveAsync(() => this._remove(pkg));
}
/**
* Check if package name is obsolete.
*
* @param pkg The package.
* @return True if package obslete, else false.
*/
async isObsolete(pkg) {
return this._exclusiveAsync(() => this._isObsolete(pkg));
}
/**
* List obsolete package names.
*
* @return A list of obsolete package names.
*/
async obsolete() {
return this._exclusiveAsync(() => this._obsolete());
}
/**
* Cleanup all obsolete and outdated packages.
*
* @return Lists of removed packages.
*/
async cleanup() {
return this._exclusiveAsync(() => this._cleanup());
}
/**
* Join path on the base path.
*
* @param parts[] Path parts.
* @return Joined path.
*/
pathTo(...parts) {
return (0, _path.join)(this.path, ...parts);
}
/**
* Join path on the meta path.
*
* @param parts[] Path parts.
* @return Joined path.
*/
pathToMeta(...parts) {
return this.pathTo(this.metaDir, ...parts);
}
/**
* Join path on the temp folder path.
*
* @param parts[] Path parts.
* @return Joined path.
*/
pathToTemp(...parts) {
return this.pathToMeta(this.tempDir, ...parts);
}
/**
* Join path on package base path.
*
* @param pkg The package.
* @param parts[] Path parts.
* @return Joined path.
*/
pathToPackage(pkg, ...parts) {
return this._exclusiveSync(() => this._pathToPackage(pkg, ...parts));
}
/**
* Join path on package meta path.
*
* @param pkg The package.
* @param parts[] Path parts.
* @return Joined path.
*/
pathToPackageMeta(pkg, ...parts) {
return this._exclusiveSync(() => this._pathToPackageMeta(pkg, ...parts));
}
/**
* Join path on package base path.
*
* @param pkg The package.
* @param parts[] Path parts.
* @return Joined path.
*/
_pathToPackage(pkg, ...parts) {
this._assertActive();
const name = this._packageToName(pkg, false);
return this.pathTo(name, ...parts);
}
/**
* Join path on package meta path.
*
* @param pkg The package.
* @param parts[] Path parts.
* @return Joined path.
*/
_pathToPackageMeta(pkg, ...parts) {
this._assertActive();
const name = this._packageToName(pkg, false);
return this.pathTo(name, this.metaDir, ...parts);
}
/**
* Obtain exclusive access for the duration of a syncronous callback.
*
* @param func Syncronous function.
* @return Return value of the syncronous callback.
*/
_exclusiveSync(func) {
this._assertNotExclusive();
this._exclusive = true;
let r;
try {
r = func.call(this, this);
} finally {
this._exclusive = false;
}
return r;
}
/**
* Obtain exclusive access for the duration of a asyncronous callback.
*
* @param func Asyncronous function.
* @return Return value of the asyncronous callback.
*/
async _exclusiveAsync(func) {
this._assertNotExclusive();
this._exclusive = true;
let r;
try {
r = await func.call(this, this);
} finally {
this._exclusive = false;
}
return r;
}
/**
* Itterate over the packages.
*/
*_packageItter() {
this._assertActive();
for (const entry of this._packages.itter()) {
this._assertActive();
yield entry;
}
}
/**
* Get package by the unique name.
*
* @param name Package name.
* @return The package or null.
*/
_packageByName(name) {
this._assertLoaded();
return this._packages.byName(name);
}
/**
* Get package by the sha256 hash.
*
* @param sha256 Package sha256.
* @return The package or null.
*/
_packageBySha256(sha256) {
this._assertLoaded();
return this._packages.bySha256(sha256);
}
/**
* Get package by the unique value.
*
* @param unique Package unique.
* @return The package or null.
*/
_packageByUnique(unique) {
this._assertLoaded();
return this._packages.byUnique(unique);
}
/**
* Check if package is in packages collection.
*
* @param pkg Package instance.
* @return If the package instance is present.
*/
_packageIsMember(pkg) {
this._assertLoaded();
return this._packages.has(pkg);
}
/**
* Get package install file.
*
* @param pkg The package.
* @return Path to install file.
*/
async _packageInstallFile(pkg) {
this._assertLoaded();
pkg = this._packageToPackage(pkg);
const data = await this._packageMetaReceiptRead(pkg);
return this._pathToPackage(pkg, data.file);
}
/**
* Verify package install file, using size and hash.
*
* @param pkg The package.
*/
async _packageInstallVerify(pkg) {
this._assertLoaded();
pkg = this._packageToPackage(pkg);
const data = await this._packageMetaReceiptRead(pkg);
const {
sha256,
file,
size
} = data;
const filePath = this._pathToPackage(pkg, file);
await (0, _util.fileSizeVerify)(filePath, size);
await (0, _util.fileHashVerify)(filePath, [{
algorithm: 'sha256',
encoding: 'hex',
digest: sha256
}]);
}
/**
* Get package object by object, name, or hash.
* If package object is passed, check that object is known.
* Throw error if package is unknown.
*
* @param pkg The package.
* @return Package object.
*/
_packageToPackage(pkg) {
this._assertLoaded();
let r;
if (typeof pkg === 'string') {
const p = this._packageByUnique(pkg);
if (!p) {
throw new Error(`Unknown package: ${pkg}`);
}
r = p;
} else {
this._assertpackageIsMember(pkg);
r = pkg;
}
return r;
}
/**
* Get package name by object, name, or hash.
* If package object is passed, check that object is known.
* If string is passed and unknown, returns string.
*
* @param pkg The package.
* @param mustExist Must exist.
* @return Package object.
*/
_packageToName(pkg, mustExist = true) {
this._assertLoaded();
let r;
if (typeof pkg === 'string') {
const pkgObj = this._packageByUnique(pkg);
if (!pkgObj && mustExist) {
throw new Error(`Unknown package: ${pkg}`);
}
r = pkgObj ? pkgObj.name : pkg;
} else {
this._assertpackageIsMember(pkg);
r = pkg.name;
}
return r;
}
/**
* List package parent packages.
*
* @param pkg The package.
* @return Packages list.
*/
_packageParents(pkg) {
this._assertLoaded();
pkg = this._packageToPackage(pkg);
const r = [];
for (let p = pkg.parent; p; p = p.parent) {
r.push(p);
}
return r;
}
/**
* List package parent packages not updated.
*
* @param pkg The package.
* @return Packages list.
*/
async _packageParentsNotUpdated(pkg) {
this._assertLoaded();
pkg = this._packageToPackage(pkg);
const r = [];
for (let p = pkg.parent; p; p = p.parent) {
if (await this._isCurrent(p)) {
break;
}
r.push(p);
}
return r;
}
/**
* List the packages that need to be installed.
*
* @param pkg The package.
* @return Package root or null and the children list.
*/
async _packageInstallList(pkg) {
this._assertLoaded();
pkg = this._packageToPackage(pkg);
const r = await this._packageParentsNotUpdated(pkg);
r.reverse().push(pkg);
return r;
}
/**
* Packages ordered by dependencies.
*
* @param pkgs Packages list.
* @return Packages list, sorted order.
*/
_packagesDependOrdered(pkgs) {
this._assertLoaded();
const list = pkgs.map(pkg => this._packageToPackage(pkg));
return (0, _util.dependSort)(list, pkg => this._packageParents(pkg));
}
/**
* Read package installed receipt.
*
* @param pkg The package.
* @return Package object.
*/
async _packageMetaReceiptRead(pkg) {
this._assertLoaded();
const name = this._packageToName(pkg, false);
const pkgf = this._pathToPackageMeta(name, this.packageFile);
const r = await (0, _util.promiseCatch)(_fsExtra.default.readJson(pkgf), null);
if (!r) {
throw new Error('Package is not installed');
}
return r;
}
/**
* Write package installed receipt.
*
* @param pkg The package.
*/
async _packageMetaReceiptWrite(pkg) {
this._assertLoaded();
pkg = this._packageToPackage(pkg);
const name = this._packageToName(pkg);
const pkgf = this._pathToPackageMeta(name, this.packageFile);
const receipt = await this._packageMetaReceiptFromPackage(pkg);
await _fsExtra.default.outputJson(pkgf, receipt, {
spaces: '\t'
});
}
/**
* CReate package installed receipt object from a package.
*
* @param pkg The package.
* @return Receipt object.
*/
async _packageMetaReceiptFromPackage(pkg) {
this._assertLoaded();
pkg = this._packageToPackage(pkg);
const r = {
name: pkg.name,
file: pkg.file,
size: pkg.size,
sha256: pkg.sha256,
source: pkg.source
};
return r;
}
/**
* Check if package meta directory exists.
*
* @param pkg The package.
* @return True if the meta directory path, else false.
*/
async _packageMetaDirExists(pkg) {
this._assertLoaded();
const dir = this._pathToPackageMeta(pkg);
return await _fsExtra.default.pathExists(dir);
}
/**
* Ensure package directory exists.
*
* @param pkg The package.
*/
async _packageDirsEnsure(pkg) {
this._assertLoaded();
pkg = this._packageToPackage(pkg);
const dir = this._pathToPackage(pkg);
const dirMeta = this._pathToPackageMeta(pkg);
await _fsExtra.default.ensureDir(dir);
await _fsExtra.default.ensureDir(dirMeta);
}
/**
* Assert instance not inited.
*/
_assertNotInited() {
if (this._inited) {
throw new Error('Instance initialized');
}
}
/**
* Assert instance is inited.
*/
_assertInited() {
if (!this._inited) {
throw new Error('Instance uninitialized');
}
}
/**
* Assert instance not destroyed.
*/
_assertNotDestroyed() {
if (this._destroyed) {
throw new Error('Instance destroyed');
}
}
/**
* Assert instance lock was not compromised.
*/
_assertLockNotCompromised() {
if (this.lockCompromised) {
throw new Error('Instance lock file compromised');
}
}
/**
* Assert instance is active.
* Implies inited, not-destroyed, and lock-not-compromised assertions.
*/
_assertActive() {
// Check everything is active in order they should report failure.
this._assertInited();
this._assertNotDestroyed();
this._assertLockNotCompromised();
}
/**
* Assert instance all loaded, including the packages list.
* Implies all active assertions.
*/
_assertLoaded() {
this._assertActive();
if (!this.loaded) {
throw new Error('Packages list not loaded');
}
}
/**
* Assert not current running exclusive method.
*/
_assertNotExclusive() {
if (this._exclusive) {
throw new Error('Already running exclusive method');
}
}
/**
* Assert the package is in packages collection.
*
* @param pkg Package instance.
*/
_assertpackageIsMember(pkg) {
this._assertLoaded();
this._packages.assertHas(pkg);
}
/**
* Assert correct status code.
*
* @param expected Expected status code.
* @param statusCode The actual status code.
*/
_assertStatusCode(expected, statusCode) {
if (statusCode === expected) {
return;
}
throw new Error(`Unexpected status code: ${statusCode} expected: ${expected}`);
}
/**
* Assert correct content length.
*
* @param expected Expected content length, as a number.
* @param contentLength The actual content length as string.
*/
_assertContentLength(expected, contentLength) {
const size = +contentLength;
if (size === expected) {
return;
}
throw new Error(`Unexpected content-length: ${contentLength} expected: ${expected}`);
}
/**
* Initialize instance.
*/
async _init() {
this._assertNotInited();
await this._ensureDirs();
await this._lock.aquire();
await this._packages.readIfExists();
this._inited = true;
}
/**
* Destroy instance.
*/
async _destroy() {
// Destroy should always work only once if instance was inited.
this._assertInited();
this._assertNotDestroyed(); // Lock may have been compromised, only release if currently held.
if (this._lock.held) {
await this._lock.release();
}
this._destroyed = true;
}
/**
* Update the package manager.
* Updates the packages list.
*
* @return Update report.
*/
async _update() {
this._assertActive();
return this._updatePackages();
}
/**
* Check if a package is installed.
*
* @param pkg The package.
* @return True if already installed, else false.
*/
async _isInstalled(pkg) {
this._assertLoaded();
pkg = this._packageToPackage(pkg);
try {
await this._packageMetaReceiptRead(pkg);
} catch (err) {
return false;
}
return true;
}
/**
* Check if a package is installed and up-to-date.
*
* @param pkg The package.
* @return True if already up-to-date, else false.
*/
async _isCurrent(pkg) {
this._assertLoaded();
pkg = this._packageToPackage(pkg);
let data = null;
try {
data = await this._packageMetaReceiptRead(pkg);
} catch (err) {
return false;
}
return !!(data.sha256 === pkg.sha256 && data.size === pkg.size && data.file === pkg.file && data.name === pkg.name);
}
/**
* List all installed packages.
*
* @return A list of installed package objects.
*/
async _installed() {
this._assertLoaded();
const dirList = await this._packagesDirList();
const filtered = await (0, _util.arrayFilterAsync)(dirList, async entry => {
const pkg = this._packageByName(entry);
return pkg && (await this._isInstalled(pkg));
});
return this._packagesDependOrdered(filtered);
}
/**
* List all outdated packages.
*
* @return The list of outdated package objects.
*/
async _outdated() {
this._assertLoaded();
const dirList = await this._packagesDirList();
const filtered = await (0, _util.arrayFilterAsync)(dirList, async entry => {
const pkg = this._packageByName(entry);
return pkg && !(await this._isCurrent(pkg));
});
return this._packagesDependOrdered(filtered);
}
/**
* An alias for upgradeSlim.
*
* @return List of packages upgraded.
*/
async _upgrade() {
return this._upgradeSlim();
}
/**
* Upgrade any outdated packages.
*
* @return List of packages upgraded.
*/
async _upgradeFull() {
this._assertLoaded();
const outdated = await this._outdated();
return this._installFullMulti(outdated);
}
/**
* Upgrade any outdated packages, using slim install method.
*
* @return List of packages upgraded.
*/
async _upgradeSlim() {
this._assertLoaded();
const outdated = await this._outdated();
return this._installSlimMulti(outdated);
}
/**
* An alias for installSlim.
*
* @param pkg The package.
* @return True if was installed, false if already installed.
*/
async _install(pkg) {
return this._installSlim(pkg);
}
/**
* Install package, with parents.
* Returns the list of packages installed to install.
* Returns empty array if current version is already installed.
*
* @param pkg The package.
* @return List of packages processed.
*/
async _installFull(pkg) {
this._assertLoaded();
pkg = this._packageToPackage(pkg); // If current version is installed, skip.
const installed = await this._isCurrent(pkg);
if (installed) {
this.eventPackageInstallCurrent.triggerSync({
package: pkg,
method: 'slim'
});
return [];
} // List packages to install.
const list = await this._packageInstallList(pkg);
const r = [];
for (const p of list) {
this.eventPackageInstallBefore.triggerSync({
package: pkg,
method: 'full'
});
const parent = p.parent;
const tmpFile = this.pathToTemp(`${p.name}.part`);
const outFile = this._pathToPackage(p, p.file);
const oldFile = (await this._isInstalled(p)) ? await this._packageInstallFile(p) : null; // Download or extract, remove temporary directory on failure.
// Write the package file last, means successful install.
try {
await this._tempDirEnsure(true);
if (parent) {
const pF = this._pathToPackage(parent, parent.file);
await this._packageExtract(p, tmpFile, pF);
} else {
await this._packageDownload(p, tmpFile);
}
await this._packageDirsEnsure(p);
if (oldFile) {
await _fsExtra.default.remove(oldFile);
}
await _fsExtra.default.move(tmpFile, outFile);
await this._packageMetaReceiptWrite(p);
} finally {
await this._tempDirRemove();
}
this.eventPackageInstallAfter.triggerSync({
package: pkg,
method: 'full'
});
r.push(p);
}
return r;
}
/**
* Install multiple package with parents, higher dependencies first.
*
* @param pkgs Packages list.
* @return Installed list.
*/
async _installFullMulti(pkgs) {
this._assertLoaded();
const list = this._packagesDependOrdered(pkgs);
return await (0, _util.arrayMapAsync)(list, async pkg => ({
package: pkg,
installed: await this._installFull(pkg)
}));
}
/**
* Install package, without parents.
* Returns the list of packages downloaded or extracted to install.
* Returns empty array if current version is already installed.
*
* @param pkg The package.
* @return List of packages processed.
*/
async _installSlim(pkg) {
this._assertLoaded();
pkg = this._packageToPackage(pkg); // If current version is installed, skip.
const installed = await this._isCurrent(pkg);
if (installed) {
this.eventPackageInstallCurrent.triggerSync({
package: pkg,
method: 'slim'
});
return [];
} // List packages to install.
const list = await this._packageInstallList(pkg); // If first root and not only, extract without downloading all.
let stream = false;
if (list.length > 1 && !list[0].parent) {
stream = true;
list.shift();
}
const outFile = this._pathToPackage(pkg, pkg.file);
const fileTmpBase = this.pathToTemp(pkg.name);
const fileTmp = i => `${fileTmpBase}.${i}.part`;
const oldFile = (await this._isInstalled(pkg)) ? await this._packageInstallFile(pkg) : null;
this.eventPackageInstallBefore.triggerSync({
package: pkg,
method: 'slim'
});
const r = [];
try {
await this._tempDirEnsure(true);
await this._packageDirsEnsure(pkg);
let i = 0;
let tmpFileP = '';
let tmpFile = '';
for (const p of list) {
tmpFile = fileTmp(i++);
const parent = p.parent; // If streaming from a root package, handle that.
if (stream) {
await this._packageStream(p, tmpFile);
stream = false;
} else {
// Use previous temp file if present.
// Else use parent file if not root file.
// A root package that is not streamed will be downloaded.
const archive = tmpFileP || (parent ? this._pathToPackage(parent, parent.file) : null);
if (archive) {
await this._packageExtract(p, tmpFile, archive);
} else {
await this._packageDownload(p, tmpFile);
}
} // Remove previous temporary file if present.
if (tmpFileP) {
await _fsExtra.default.remove(tmpFileP);
}
tmpFileP = tmpFile;
r.push(p);
} // Move the final file into place and write package file.
// Write the package file last, means successful install.
await this._packageDirsEnsure(pkg);
if (oldFile) {
await _fsExtra.default.remove(oldFile);
}
await _fsExtra.default.move(tmpFile, outFile);
await this._packageMetaReceiptWrite(pkg);
} finally {
await this._tempDirRemove();
}
this.eventPackageInstallAfter.triggerSync({
package: pkg,
method: 'slim'
});
return r;
}
/**
* Install multiple package without parents, higher dependencies first.
*
* @param pkgs Packages list.
* @return Installed list.
*/
async _installSlimMulti(pkgs) {
this._assertLoaded();
const list = this._packagesDependOrdered(pkgs);
return await (0, _util.arrayMapAsync)(list, async pkg => ({
package: pkg,
installed: await this._installSlim(pkg)
}));
}
/**
* Remove package.
*
* @param pkg The package.
* @return True if removed, false if nothing to remove.
*/
async _remove(pkg) {
this._assertLoaded();
const dir = this._pathToPackage(pkg);
const stat = await (0, _util.lstatExists)(dir);
if (!stat) {
return false;
}
const dirMeta = this._pathToPackageMeta(pkg); // Remove meta directory first, avoid partial installed state.
await _fsExtra.default.remove(dirMeta);
await _fsExtra.default.remove(dir);
return true;
}
/**
* Check if package name is obsolete.
*
* @return True if package obslete, else false.
*/
async _isObsolete(pkg) {
this._assertLoaded();
return !this._packageByName(pkg) && (await this._packageMetaDirExists(pkg));
}
/**
* List obsolete package names.
*
* @return A list of obsolete package names.
*/
async _obsolete() {
this._assertLoaded();
const dirList = await this._packagesDirList();
return (0, _util.arrayFilterAsync)(dirList, entry => this._isObsolete(entry));
}
/**
* Cleanup all obsolete and outdated packages.
*
* @return Lists of removed packages.
*/
async _cleanup() {
this._assertLoaded(); // Remove the temporary directory if present.
await this._tempDirRemove(); // Remove the obsolete packages.
const obsolete = await this._obsolete();
return await (0, _util.arrayMapAsync)(obsolete, async pkg => {
this.eventPackageCleanupBefore.triggerSync({
package: pkg
});
const removed = await this._remove(pkg);
this.eventPackageCleanupAfter.triggerSync({
package: pkg,
removed
});
return {
package: pkg,
removed
};
});
}
/**
* List all packages in the directory.
* Only those directories with the meta directory are returned.
* Dot directories are also always skipped.
*
* @return List of all recognized package directories.
*/
async _packagesDirList() {
this._assertLoaded();
const dirList = await (0, _util.readDir)(this.path, false);
return (0, _util.arrayFilterAsync)(dirList, entry => this._packageMetaDirExists(entry));
}
/**
* Extract package from archive path.
*
* @param pkg The package.
* @param file Out file.
* @param archive Archive file.
*/
async _packageExtract(pkg, file, archive) {
this._assertLoaded();
pkg = this._packageToPackage(pkg);
const zip = this._createZip();
await zip.openFile(archive);
await this._packageExtractZip(pkg, file, zip);
}
/**
* Extract package from zip instance.
*
* @param pkg The package.
* @param file Out file.
* @param archive Archive file.
*/
async _packageExtractZip(pkg, file, zip) {
this._assertLoaded();
const pkgO = pkg = this._packageToPackage(pkg);
const {
source,
size,
sha256
} = pkg;
this.eventPackageExtractBefore.triggerSync({
package: pkgO
});
let found = false;
await zip.read(async entry => {
if (entry.path !== source) {
return false;
}
let read = 0;
this.eventPackageExtractProgress.triggerSync({
package: pkgO,
total: size,
amount: 0
});
await (0, _util.zipEntryExtract)(entry, file, size, [{
algorithm: 'sha256',
encoding: 'hex',
digest: sha256
}], data => {
read += data.length;
this.eventPackageExtractProgress.triggerSync({
package: pkgO,
total: size,
amount: read
});
});
found = true;
return true;
});
if (!found) {
throw new Error(`Failed to locate for extraction: ${source}`);
}
this.eventPackageExtractAfter.triggerSync({
package: pkgO
});
}
/**
* Download package.
*
* @param pkg The package.
* @param file Out file.
*/
async _packageDownload(pkg, file) {
this._assertLoaded();
const pkgO = pkg = this._packageToPackage(pkg);
const {
size,
sha256
} = pkg;
this.eventPackageDownloadBefore.triggerSync({
package: pkgO
});
let read = 0;
await (0, _util.streamRequestDownload)(this._request.stream({
url: pkg.source
}), file, size, [{
algorithm: 'sha256',
encoding: 'hex',
digest: sha256
}], response => {
const {
statusCode,
headers
} = response;
const contentLength = headers['content-length'];
this._assertStatusCode(200, statusCode);
if (contentLength) {
this._assertContentLength(size, contentLength);
}
this.eventPackageDownloadProgress.triggerSync({
package: pkgO,
total: size,
amount: 0
});
}, data => {
read += data.length;
this.eventPackageDownloadProgress.triggerSync({
package: pkgO,
total: size,
amount: read
});
});
this.eventPackageDownloadAfter.triggerSync({
package: pkgO
});
}
/**
* Stream package out of root package.
*
* @param pkg The package.
* @param file Out file.
*/
async _packageStream(pkg, file) {
this._assertLoaded();
pkg = this._packageToPackage(pkg);
const parent = pkg.parent;
if (!parent || parent.parent) {
throw new Error('Can only stream direct children of root packages');
} // Extract from zip without downloading the full zip.
const streamer = this._packageStreamStreamer(parent);
const zip = this._createZip();
await zip.openStreamer(streamer, parent.size);
await this._packageExtractZip(pkg, file, zip);
}
/**
* Create streamer function for a package.
* Only works for a root package.
*
* @param pkg The package.
* @return Streamer function.
*/
_packageStreamStreamer(pkg) {
this._assertLoaded();
const pkgO = pkg = this._packageToPackage(pkg);
const {
source
} = pkg;
return (start, end) => {
const size = end - start;
this.eventPackageStreamBefore.triggerSync({
package: pkgO
});
let read = 0;
const stream = this._request.stream({
url: source,
headers: {
Range: `bytes=${start}-${end - 1}`
}
});
(0, _util.streamRequest)(stream, null, null, response => {
const {
statusCode,
headers
} = response;
const contentLength = headers['content-length'];
this._assertStatusCode(206, statusCode);
if (contentLength) {
this._assertContentLength(size, contentLength);
}
this.eventPackageStreamProgress.triggerSync({
package: pkgO,
total: size,
amount: 0
});
}, data => {
read += data.length;
this.eventPackageStreamProgress.triggerSync({
package: pkgO,
total: size,
amount: read
});
}).then(() => {
this.eventPackageStreamAfter.triggerSync({
package: pkgO
});
}).catch(() => {// Do nothing, let ZIP library handle stream errors.
}); // Workaround for type issue.
return stream;
};
}
/**
* Request the packages file.
*
* @return File contents as string.
*/
async _requestPackages() {
this._assertActive(); // Headers and gzip to avoid compression and reduce transfer size.
const {
response,
body
} = await this._request.promise({
url: this.packagesUrl,
headers: {
'Cache-Control': 'max-age=0',
Pragma: 'no-cache'
},
gzip: true
});
const {
statusCode
} = response;
this._assertStatusCode(200, statusCode);
if (typeof body !== 'string') {
throw new Error(`Unexpected response body type: ${typeof body}`);
}
return body;
}
/**
* Update the packages list.
*
* @return Update report.
*/
async _updatePackages() {
this._assertActive(); // Read data, update list, write list to file, return report.
const data = await this._requestPackages();
const report = this._packages.update(data);
await this._packages.write();
return report;
}
/**
* Ensure base directories exists.
*/
async _ensureDirs() {
await _fsExtra.default.ensureDir(this.path);
await _fsExtra.default.ensureDir(this.pathMeta);
}
/**
* Ensure temp directory exists.
*
* @param clean Clean existing.
*/
async _tempDirEnsure(clean = false) {
if (clean) {
await this._tempDirRemove();
}
await _fsExtra.default.ensureDir(this.pathToTemp());
}
/**
* Ensure temp directory removed.
*/
async _tempDirRemove() {
await _fsExtra.default.remove(this.pathToTemp());
}
/**
* Create the main path.
*
* @param path The path, defaults to environment variable or relative.
* @return Main path.
*/
_createPath(path) {
// Use specified, or environment variable, or relative default.
return path || process.env[this._pathEnv] || this._mainDir;
}
/**
* Create the packages URL.
*
* @param defaultUrl The default URL if the environment variable not set.
* @return Packages URL.
*/
_createPackagesUrl(defaultUrl) {
return process.env[this._packagesUrlEnv] || defaultUrl;
}
/**
* Create the Lock instance.
*
* @return Lock instance.
*/
_createLock() {
return new _lock.Lock(this.pathMeta);
}
/**
* Create the Packages instance.
*
* @return Packages instance.
*/
_createPackages() {
return new _packages.Packages(this.pathMetaPackages);
}
/**
* Create the Request instance.
*
* @return Request instance.
*/
_createRequest() {
return new _request.Request();
}
/**
* Create a Zip instance.
*
* @return Zip instance.
*/
_createZip() {
return new _zip.Zip();
}
}, _temp), (_descriptor = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_packagesUrl", [_dec], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return _constants.PACKAGES_URL;
}
}), _descriptor2 = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_packagesFile", [_dec2], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return _constants.PACKAGES_FILE;
}
}), _descriptor3 = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_packageFile", [_dec3], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return _constants.PACKAGE_FILE;
}
}), _descriptor4 = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_mainDir", [_dec4], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return _constants.MAIN_DIR;
}
}), _descriptor5 = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_metaDir", [_dec5], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return _constants.META_DIR;
}
}), _descriptor6 = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_tempDir", [_dec6], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return _constants.TEMP_DIR;
}
}), _descriptor7 = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_pathEnv", [_dec7], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return _constants.PATH_ENV;
}
}), _descriptor8 = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_packagesUrlEnv", [_dec8], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return _constants.PACKAGES_URL_ENV;
}
}), _descriptor9 = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_inited", [_dec9], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return false;
}
}), _descriptor10 = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_destroyed", [_dec10], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return false;
}
}), _descriptor11 = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_exclusive", [_dec11], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return false;
}
}), _descriptor12 = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_path", [_dec12], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _descriptor13 = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_lock", [_dec13], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _descriptor14 = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_packages", [_dec14], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _descriptor15 = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_request", [_dec15], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
})), _class));
exports.Manager = Manager;
//# sourceMappingURL=manager.js.map