UNPKG

@shockpkg/core

Version:
2,186 lines (1,729 loc) 47.7 kB
"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