verdaccio
Version:
A lightweight private npm proxy registry
837 lines (801 loc) • 111 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _assert = _interopRequireDefault(require("assert"));
var _async = _interopRequireDefault(require("async"));
var _debug = _interopRequireDefault(require("debug"));
var _lodash = _interopRequireDefault(require("lodash"));
var _stream = _interopRequireDefault(require("stream"));
var _config = require("@verdaccio/config");
var _core = require("@verdaccio/core");
var _loaders = require("@verdaccio/loaders");
var _localStorageLegacy = _interopRequireDefault(require("@verdaccio/local-storage-legacy"));
var _searchIndexer = require("@verdaccio/search-indexer");
var _streams = require("@verdaccio/streams");
var _logger = require("../lib/logger");
var _constants = require("./constants");
var _localStorage = _interopRequireDefault(require("./local-storage"));
var _metadataUtils = require("./metadata-utils");
var _storageUtils = require("./storage-utils");
var _upStorage = _interopRequireDefault(require("./up-storage"));
var _uplinkUtil = require("./uplink-util");
var _utils = require("./utils");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const debug = (0, _debug.default)('verdaccio:storage');
class Storage {
constructor(config) {
this.config = config;
this.uplinks = (0, _uplinkUtil.setupUpLinks)(config);
this.logger = _logger.logger;
// @ts-ignore
this.localStorage = null;
}
/**
* Initialize the storage: load the storage plugin (or the default
* local storage) and the filter plugins. Safe to call once; subsequent
* calls only reload missing filters.
* @param {Config} config verdaccio configuration
* @param {IPluginFilters} filters preloaded filter plugins; when omitted they are loaded from the configuration
*/
async init(config, filters) {
if (this.localStorage === null) {
this.filters = filters;
const storageInstance = await this.loadStorage(config, this.logger);
this.localStorage = new _localStorage.default(this.config, _logger.logger, storageInstance);
await this.localStorage.getSecret(config);
debug('initialization completed');
} else {
debug('storage has been already initialized');
}
if (!this.filters) {
this.filters = await (0, _loaders.asyncLoadPlugin)(this.config.filters, {
config: this.config,
logger: this.logger
}, plugin => {
return typeof plugin.filter_metadata !== 'undefined';
}, true, this.config?.serverSettings?.pluginPrefix, _core.PLUGIN_CATEGORY.FILTER);
debug('filters available %o', this.filters.length);
}
}
/**
* Resolve the storage backend: a configured storage plugin when available,
* otherwise the default `@verdaccio/local-storage` on the configured path.
* @return {Promise<StoragePlugin>} the storage plugin instance
*/
async loadStorage(config, logger) {
const Storage = await this.loadStorePlugin();
if (_lodash.default.isNil(Storage)) {
(0, _assert.default)(this.config.storage, 'CONFIG: storage path not defined');
debug('no custom storage found, loading default storage @verdaccio/local-storage');
const localStorage = new _localStorageLegacy.default(config, logger);
logger.info({
name: '@verdaccio/local-storage',
pluginCategory: _core.PLUGIN_CATEGORY.STORAGE
}, 'plugin @{name} successfully loaded (@{pluginCategory})');
return localStorage;
}
return Storage;
}
/**
* Load the storage plugins declared under `store` in the configuration.
* Only one storage is supported: with several plugins configured the
* first loaded one wins and a warning is logged.
* @return {Promise<StoragePluginLegacy<Config> | undefined>} the selected plugin, or undefined when none is configured
*/
async loadStorePlugin() {
const plugins = await (0, _loaders.asyncLoadPlugin)(this.config.store, {
config: this.config,
logger: this.logger
}, plugin => {
return typeof plugin.getPackageStorage !== 'undefined';
}, true, this.config?.serverSettings?.pluginPrefix, _core.PLUGIN_CATEGORY.STORAGE);
if (plugins.length > 1) {
this.logger.warn('more than one storage plugins has been detected, multiple storage are not supported, one will be selected automatically');
}
return _lodash.default.head(plugins);
}
/**
* Add a package to the system (publish flow).
* Verifies the package does not exist locally nor on any uplink before
* creating it locally; uplinks being offline rejects the publish unless
* `publish.allow_offline` is enabled.
* Used storages: local (write) && uplinks
* @param {String} name package name
* @param {Object} metadata package manifest to publish
* @param {Function} callback invoked with an error when the publish is rejected
*/
async addPackage(name, metadata, callback) {
debug('add package %o', name);
try {
await (0, _storageUtils.checkPackageLocal)(name, this.localStorage);
await (0, _storageUtils.checkPackageRemote)(name, this._isAllowPublishOffline(), this._syncUplinksMetadata.bind(this));
await (0, _storageUtils.publishPackage)(name, metadata, this.localStorage);
callback();
} catch (err) {
callback(err);
}
}
/**
* Whether `publish.allow_offline` is enabled in the configuration.
* @return {Boolean} true when publishing with unreachable uplinks is allowed
*/
_isAllowPublishOffline() {
return typeof this.config.publish !== 'undefined' && _lodash.default.isBoolean(this.config.publish.allow_offline) && this.config.publish.allow_offline;
}
/**
* Read the npm tokens matching the filter from the token storage.
* Used storages: local (read)
*/
readTokens(filter) {
return this.localStorage.readTokens(filter);
}
/**
* Save an npm token to the token storage.
* Used storages: local (write)
*/
saveToken(token) {
return this.localStorage.saveToken(token);
}
/**
* Delete an npm token from the token storage.
* Used storages: local (write)
*/
deleteToken(user, tokenKey) {
return this.localStorage.deleteToken(user, tokenKey);
}
/**
* Add a new version of a package to the system.
* Used storages: local (write)
* @param {String} name package name
* @param {String} version version id, eg. 1.0.0
* @param {Version} metadata version metadata
* @param {String} tag dist-tag pointing to the version
* @param {Function} callback
*/
addVersion(name, version, metadata, tag, callback) {
debug('add version %s package for %s', version, name);
this.localStorage.addVersion(name, version, metadata, tag, callback);
}
/**
* Tag package versions with the provided dist-tags.
* Used storages: local (write)
* @param {String} name package name
* @param {MergeTags} tagHash dist-tag to version mapping to merge
* @param {Function} callback
*/
mergeTags(name, tagHash, callback) {
debug('merge tags for %o', name);
this.localStorage.mergeTags(name, tagHash, callback);
}
/**
* Change an existing package (eg. unpublish one version).
* Used storages: local (write)
* @param {String} name package name
* @param {Manifest} metadata updated package manifest
* @param {String} revision expected revision of the stored manifest
* @param {Function} callback
*/
changePackage(name, metadata, revision, callback) {
debug('change existing package for package %o revision %o', name, revision);
this.localStorage.changePackage(name, metadata, revision, callback);
}
/**
* Remove a package from the local storage and the search indexer.
* Used storages: local (write)
* @param {String} name package name
* @param {Function} callback
*/
removePackage(name, callback) {
debug('remove package %o', name);
this.localStorage.removePackage(name, callback);
// update the indexer
_searchIndexer.SearchMemoryIndexer.remove(name).catch(reason => {
debug('indexer has failed on remove item %o', reason);
_logger.logger.error('indexer has failed on remove item');
});
}
/**
* Remove a tarball from the local storage. The tarball must not be
* linked by any existing version, ie. the version should be
* unpublished first.
* Used storages: local (write)
* @param {String} name package name
* @param {String} filename tarball file name
* @param {String} revision expected revision of the stored manifest
* @param {Function} callback
*/
removeTarball(name, filename, revision, callback) {
debug('remove tarball %s for %s', filename, name);
this.localStorage.removeTarball(name, filename, revision, callback);
}
/**
* Upload a tarball (publish flow). Synchronous, returns a writable
* stream the tarball body is piped into.
* Used storages: local (write)
* @param {String} name package name
* @param {String} filename tarball file name
* @return {Stream} writable upload stream
*/
addTarball(name, filename) {
debug('add a tarball for %o', name);
return this.localStorage.addTarball(name, filename);
}
/**
* Whether a tarball exists in the local storage, without reading it
* (the stream is aborted as soon as it opens).
* Used storages: local (read)
* @param {String} name package name
* @param {String} filename tarball file name
* @return {Promise<Boolean>} true when the tarball is stored locally
*/
hasLocalTarball(name, filename) {
const self = this;
return new Promise((resolve, reject) => {
let localStream = self.localStorage.getTarball(name, filename);
let isOpen = false;
localStream.on('error', err => {
if (isOpen || err.status !== _constants.HTTP_STATUS.NOT_FOUND) {
reject(err);
}
// local reported 404 or request was aborted already
if (localStream) {
localStream.abort();
localStream = null;
}
resolve(false);
});
localStream.on('open', function () {
isOpen = true;
localStream.abort();
localStream = null;
resolve(true);
});
});
}
/**
* Get a tarball. Synchronous, returns a readable stream.
* The tarball is read locally first; on a local miss the distfile record
* is resolved from the package metadata (see lookupDistFile), syncing the
* uplinks when the local metadata does not know the file, and the tarball
* is then fetched from the uplink that serves the distfile url.
* Used storages: local || uplink (just one)
* @param {String} name package name
* @param {String} filename tarball file name
* @return {Stream} readable tarball stream
*/
getTarball(name, filename) {
debug('get tarball for package %o filename %o', name, filename);
const readStream = new _streams.ReadTarball({});
readStream.abort = function () {};
const self = this;
// Check if the tarball is allowed by filter plugins before serving.
// Filters may block specific versions, so we verify the tarball's version
// still exists in the filtered metadata.
this._isTarballAllowedByFilters(name, filename).then(async allowed => {
if (!allowed) {
readStream.emit('error', _utils.ErrorCode.getNotFound(_constants.API_ERROR.NO_PACKAGE));
return;
}
// trying local first
let localStream = self.localStorage.getTarball(name, filename);
let isOpen = false;
localStream.on('error', err => {
if (isOpen || err.status !== _constants.HTTP_STATUS.NOT_FOUND) {
return readStream.emit('error', err);
}
// local reported 404
const err404 = err;
localStream.abort();
localStream = null; // we force for garbage collector
const lookupFromUplinks = info => {
self._syncUplinksMetadata(name, info, {}, (syncErr, syncInfo) => {
if (_lodash.default.isNil(syncErr) === false) {
return readStream.emit('error', syncErr);
}
// _syncUplinksMetadata returns filter-applied metadata; if the
// version was removed by a filter, surface a 404 like a missing tarball.
if (self.filters?.length && !(0, _utils.hasTarball)(syncInfo, filename)) {
return readStream.emit('error', err404);
}
const distFile = (0, _storageUtils.lookupDistFile)(syncInfo, filename);
if (_lodash.default.isNil(distFile)) {
debug('remote tarball not found');
return readStream.emit('error', err404);
}
debug('dist file found, using it %o', distFile.url);
serveFile(distFile);
});
};
self.localStorage.getPackageMetadataAsync(name).then(info => {
const distFile = (0, _storageUtils.lookupDistFile)(info, filename);
if (_lodash.default.isNil(distFile) === false) {
// information about this file exists locally
debug('dist file found, using it %o', distFile.url);
serveFile(distFile);
} else {
// we know nothing about this file, trying to get information elsewhere
debug('dist file not found, proceed update upstream');
lookupFromUplinks(info);
}
}, () => {
// we know nothing about this file, trying to get information elsewhere
lookupFromUplinks(null);
});
});
localStream.on('content-length', function (v) {
readStream.emit('content-length', v);
});
localStream.on('open', function () {
isOpen = true;
localStream.pipe(readStream);
});
});
return readStream;
/**
* Stream the tarball from the uplink serving the distfile, caching it
* locally (and restoring the distfile record) when the uplink has the
* cache enabled.
* @param {DistFile} file distfile record resolved for the tarball
*/
function serveFile(file) {
let uplink = null;
if (file.registry && self.uplinks[file.registry]) {
// the distfile records which uplink it was merged from
// (see LocalStorage._updateUplinkToRemoteProtocol)
uplink = self.uplinks[file.registry];
debug('tarball %o is served by the recorded uplink %o', filename, file.registry);
} else {
const candidates = [];
for (const uplinkId in self.uplinks) {
if ((0, _config.hasProxyTo)(name, uplinkId, self.config.packages)) {
candidates.push(self.uplinks[uplinkId]);
}
}
debug('tarball %o has %o candidate uplinks', filename, candidates.length);
// pick the uplink that actually serves the distfile url, so the
// request carries the settings of the registry hosting it; with
// several matching uplinks (duplicated urls) the last configured
// one keeps winning, exactly like the previous selection did
for (const candidate of candidates) {
if (candidate.isUplinkValid(file.url)) {
uplink = candidate;
}
}
if (uplink !== null) {
debug('uplink %o url matches tarball %o', uplink.upname, file.url);
} else if (candidates.length === 1) {
// a single configured uplink keeps the legacy behavior: tarballs
// hosted elsewhere (a CDN) may still need its agent/proxy settings
uplink = candidates[0];
debug('using the single configured uplink %o for tarball %o hosted elsewhere', uplink.upname, file.url);
}
}
if (uplink == null) {
debug('upstream not found, creating one for %o', name);
uplink = new _upStorage.default({
url: file.url,
cache: true,
_autogenerated: true
}, self.config);
}
let savestream = null;
if (uplink.config.cache) {
// persist which configured uplink served the tarball, so future
// fetches can pick it directly (autogenerated proxies have no name)
debug('cache remote tarball enabled');
let distFile = file;
if (!file.registry && uplink.upname) {
debug('recording uplink %o on the persisted distfile for %o', uplink.upname, filename);
distFile = {
...file,
registry: uplink.upname
};
}
savestream = self.localStorage.addTarball(name, filename, distFile);
} else {
debug('cache remote tarball disabled');
}
let on_open = function () {
// prevent it from being called twice
on_open = function () {};
const rstream2 = uplink.fetchTarball(file.url);
rstream2.on('error', function (err) {
if (savestream) {
savestream.abort();
}
savestream = null;
readStream.emit('error', err);
});
rstream2.on('end', function () {
if (savestream) {
savestream.done();
}
});
rstream2.on('content-length', function (v) {
readStream.emit('content-length', v);
if (savestream) {
savestream.emit('content-length', v);
}
});
rstream2.pipe(readStream);
if (savestream) {
rstream2.pipe(savestream);
}
};
if (savestream) {
savestream.on('open', function () {
on_open();
});
savestream.on('error', function (err) {
self.logger.warn({
err: err,
fileName: file
}, 'error saving file @{fileName}: @{err.message}\n@{err.stack}');
if (savestream) {
savestream.abort();
}
savestream = null;
on_open();
});
} else {
on_open();
}
}
}
/**
* Retrieve the package metadata: the local manifest merged with the
* manifest of every uplink with proxy access to the package.
* Used storages: local && uplink (proxy_access)
* @param {Object} options
* @property {String} options.name package name
* @property {Object} options.req Express `req` object
* @property {Boolean} options.keepUpLinkData keep the uplink info (last update, etc.) in the package metadata
* @property {Boolean} options.abbreviated serve the abbreviated manifest (npm install)
* @property {Function} options.callback invoked with the merged manifest and any uplink errors
*/
getPackage(options) {
debug('get package for %o', options.name);
this.localStorage.getPackageMetadata(options.name, (err, data) => {
if (err && (!err.status || err.status >= _constants.HTTP_STATUS.INTERNAL_ERROR)) {
// report internal errors right away
return options.callback(err);
}
this._syncUplinksMetadata(options.name, data, {
req: options.req,
uplinksLook: options.uplinksLook
}, function getPackageSynUpLinksCallback(err, result, uplinkErrors) {
if (err) {
return options.callback(err);
}
(0, _utils.normalizeDistTags)((0, _storageUtils.cleanUpLinksRef)(options.keepUpLinkData, result));
// npm can throw if this field doesn't exist
result._attachments = {};
if (options.abbreviated === true) {
debug('get abbreviated manifest');
options.callback(null, (0, _storageUtils.convertAbbreviatedManifest)(result), uplinkErrors);
} else {
debug('get full package manifest');
options.callback(null, result, uplinkErrors);
}
});
});
}
/**
* Retrieve remote and local packages more recent than the start key.
* All uplinks are streamed first, then the local packages; local
* packages can override registry ones just because they appear in the
* JSON last — a trade-off made to avoid memory issues.
* Used storages: local && uplink (proxy_access)
* @param {String} startkey timestamp to search from
* @param {Object} options request options; `local=1` in the query skips the uplinks
* @return {Stream} object stream of search results
*/
search(startkey, options) {
const self = this;
const searchStream = new _stream.default.PassThrough({
objectMode: true
});
_async.default.eachSeries(Object.keys(this.uplinks), function (up_name, cb) {
// shortcut: if `local=1` is supplied, don't call uplinks
if (options.req?.query?.local !== undefined) {
return cb();
}
_logger.logger.info(`search for uplink ${up_name}`);
// search by keyword for each uplink
const uplinkStream = self.uplinks[up_name].search(options);
// join uplink stream with streams PassThrough
uplinkStream.pipe(searchStream, {
end: false
});
uplinkStream.on('error', function (err) {
self.logger.error({
err: err
}, 'uplink error: @{err.message}');
cb();
// to avoid call callback more than once
cb = function () {};
});
uplinkStream.on('end', function () {
cb();
// to avoid call callback more than once
cb = function () {};
});
searchStream.abort = function () {
if (uplinkStream.abort) {
uplinkStream.abort();
}
cb();
// to avoid call callback more than once
cb = function () {};
};
},
// executed after all series
function () {
// attach a local search results
const localSearchStream = self.localStorage.search(startkey, options);
searchStream.abort = function () {
localSearchStream.abort();
};
localSearchStream.pipe(searchStream, {
end: true
});
localSearchStream.on('error', function (err) {
self.logger.error({
err: err
}, 'search error: @{err.message}');
searchStream.end();
});
});
return searchStream;
}
/**
* Retrieve only private local packages, as the latest version of each.
* Used storages: local (read)
* @param {Function} callback invoked with the list of latest versions
*/
getLocalDatabase(callback) {
this.localStorage.storagePlugin.get((err, locals) => {
if (err) {
return callback(err);
}
this._collectLocalPackages(locals).then(packages => callback(null, packages), err => callback(err));
});
}
/**
* Read each local package name, apply filters, and collect the latest version.
*/
async _collectLocalPackages(locals) {
const packages = [];
for (const name of locals) {
try {
const pkgMetadata = await this.localStorage.getPackageMetadataAsync(name);
const {
filteredPackage
} = await this._applyFilters(pkgMetadata);
const latest = filteredPackage[_constants.DIST_TAGS]?.latest;
if (latest && filteredPackage.versions[latest]) {
const version = filteredPackage.versions[latest];
const timeList = filteredPackage.time;
const time = timeList[latest];
// @ts-ignore
version.time = time;
// Add for stars api
// @ts-ignore
version.users = filteredPackage.users;
packages.push(version);
} else {
this.logger.warn({
package: name
}, 'package @{package} does not have a "latest" tag?');
}
} catch (err) {
this.logger.error({
err,
package: name
}, 'error reading package @{package}');
}
}
return packages;
}
/**
* Fetch the package metadata from every uplink with proxy access and
* synchronize it with the local data; filter plugins are applied to the
* merged result.
* @param {String} name package name
* @param {Manifest} packageInfo local manifest; MUST be provided when the package exists locally
* @param {ISyncUplinks} options uplink request options
* @param {Function} callback invoked with (err, mergedManifest, uplinkErrors)
*/
_syncUplinksMetadata(name, packageInfo, options, callback) {
let found = true;
const self = this;
const upLinks = [];
const hasToLookIntoUplinks = _lodash.default.isNil(options.uplinksLook) || options.uplinksLook;
debug('sync uplinks for %o', name);
debug('is sync uplink enabled %o', hasToLookIntoUplinks);
if (!packageInfo) {
debug('local package %s not found', name);
found = false;
packageInfo = (0, _storageUtils.generatePackageTemplate)(name);
}
for (const uplink in this.uplinks) {
if ((0, _config.hasProxyTo)(name, uplink, this.config.packages) && hasToLookIntoUplinks) {
upLinks.push(this.uplinks[uplink]);
}
}
debug('uplinks found for %o: %o', name, upLinks.length);
_async.default.map(upLinks, (upLink, cb) => {
const _options = Object.assign({}, options);
const upLinkMeta = packageInfo._uplinks[upLink.upname];
if ((0, _utils.isObject)(upLinkMeta)) {
const fetched = upLinkMeta.fetched;
if (fetched && Date.now() - fetched < upLink.maxage) {
debug('returning cached manifest for %o', upLink.upname);
return cb();
}
_options.etag = upLinkMeta.etag;
}
upLink.getRemoteMetadata(name, _options, (err, upLinkResponse, eTag) => {
if (err && err.remoteStatus === 304) {
debug('uplink %o responded 304 for %o, cache is up to date', upLink.upname, name);
upLinkMeta.fetched = Date.now();
}
if (err || !upLinkResponse) {
debug('error captured on uplink %o for %o: %o', upLink.upname, name, err?.message);
return cb(null, [err || _utils.ErrorCode.getInternalError('no data')]);
}
try {
upLinkResponse = _core.validationUtils.normalizeMetadata(upLinkResponse, name);
} catch (err) {
self.logger.error({
sub: 'out',
err: err
}, 'package.json validating error @{!err.message}\n@{err.stack}');
return cb(null, [err]);
}
packageInfo._uplinks[upLink.upname] = {
etag: eTag,
fetched: Date.now()
};
packageInfo = (0, _storageUtils.mergeUplinkTimeIntoLocal)(packageInfo, upLinkResponse);
(0, _uplinkUtil.updateVersionsHiddenUpLink)(upLinkResponse.versions, upLink);
try {
(0, _metadataUtils.mergeVersions)(packageInfo, upLinkResponse);
} catch (err) {
self.logger.error({
sub: 'out',
err: err
}, 'package.json parsing error @{!err.message}\n@{err.stack}');
return cb(null, [err]);
}
// if we got to this point, assume that the correct package exists
// on the uplink
debug('syncing on uplink %o', upLink.upname);
found = true;
cb();
});
},
// @ts-ignore
async (err, upLinksErrors) => {
(0, _assert.default)(!err && Array.isArray(upLinksErrors));
// Check for connection timeout or reset errors with uplink(s)
// (these should be handled differently from the package not being found)
if (!found) {
let uplinkTimeoutError;
for (let i = 0; i < upLinksErrors.length; i++) {
if (upLinksErrors[i]) {
for (let j = 0; j < upLinksErrors[i].length; j++) {
if (upLinksErrors[i][j]) {
const code = upLinksErrors[i][j].code;
if (code === 'ETIMEDOUT' || code === 'ESOCKETTIMEDOUT' || code === 'ECONNRESET') {
uplinkTimeoutError = true;
break;
}
}
}
}
}
if (uplinkTimeoutError) {
debug('uplinks sync failed with timeout error');
return callback(_utils.ErrorCode.getServiceUnavailable(), null, upLinksErrors);
}
debug('uplinks sync failed with no package found');
return callback(_utils.ErrorCode.getNotFound(_constants.API_ERROR.NO_PACKAGE), null, upLinksErrors);
}
if (upLinks.length === 0) {
debug('no uplinks found for %o, upstream update aborted', name);
const {
filteredPackage,
filterErrors
} = await self._applyFilters(packageInfo);
return callback(null, filteredPackage, filterErrors);
}
try {
const packageJsonLocal = await self.localStorage.updateVersionsAsync(name, packageInfo);
const {
filteredPackage,
filterErrors
} = await self._applyFilters(packageJsonLocal);
callback(null, filteredPackage, _lodash.default.concat(upLinksErrors, filterErrors));
} catch (err) {
callback(err);
}
});
}
/**
* Apply all configured filter plugins to a package manifest sequentially.
* Each filter's output is passed as input to the next filter.
* Returns the filtered manifest and any errors that occurred.
*/
async _applyFilters(packageInfo) {
const filterErrors = [];
let filteredPackage = packageInfo;
for (const filter of this.filters ?? []) {
try {
filteredPackage = await filter.filter_metadata(filteredPackage);
} catch (err) {
debug('filter plugin has failed on %o: %o', packageInfo.name, err?.message);
filterErrors.push(err);
}
}
return {
filteredPackage,
filterErrors
};
}
/**
* Check if a tarball should be served based on filter plugins, using only
* local metadata. Returns true (defer) when the version isn't cached locally
* — the uplink-sync path in getTarball re-checks filters after sync, which
* avoids a redundant uplink round-trip and double filter application.
*/
async _isTarballAllowedByFilters(name, filename) {
if (!this.filters?.length) {
return true;
}
try {
const pkgMetadata = await this.localStorage.getPackageMetadataAsync(name);
if (!(0, _utils.hasTarball)(pkgMetadata, filename)) {
return true;
}
const {
filteredPackage
} = await this._applyFilters(pkgMetadata);
const allowed = (0, _utils.hasTarball)(filteredPackage, filename);
if (allowed === false) {
debug('tarball %o of %o is blocked by a filter plugin', filename, name);
}
return allowed;
} catch (err) {
if (err?.status === _constants.HTTP_STATUS.NOT_FOUND) {
return true;
}
this.logger.error({
package: name,
fileName: filename,
err
}, 'error checking filters for tarball @{fileName} of package @{package}: @{err.message}');
return true;
}
}
/**
* Tag each version with the uplink it was fetched from, under a hidden
* (symbol) key. The local storage uses the tag to record the registry
* on the distfile records it creates.
* @param {Versions} versions versions fetched from the uplink
* @param {ProxyStorage} upLink uplink the versions were fetched from
*/
_updateVersionsHiddenUpLink(versions, upLink) {
for (const i in versions) {
if (Object.prototype.hasOwnProperty.call(versions, i)) {
const version = versions[i];
// holds a "hidden" value to be used by the package storage.
version[Symbol.for('__verdaccio_uplink')] = upLink.upname;
}
}
}
}
var _default = exports.default = Storage;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYXNzZXJ0IiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfYXN5bmMiLCJfZGVidWciLCJfbG9kYXNoIiwiX3N0cmVhbSIsIl9jb25maWciLCJfY29yZSIsIl9sb2FkZXJzIiwiX2xvY2FsU3RvcmFnZUxlZ2FjeSIsIl9zZWFyY2hJbmRleGVyIiwiX3N0cmVhbXMiLCJfbG9nZ2VyIiwiX2NvbnN0YW50cyIsIl9sb2NhbFN0b3JhZ2UiLCJfbWV0YWRhdGFVdGlscyIsIl9zdG9yYWdlVXRpbHMiLCJfdXBTdG9yYWdlIiwiX3VwbGlua1V0aWwiLCJfdXRpbHMiLCJlIiwiX19lc01vZHVsZSIsImRlZmF1bHQiLCJkZWJ1ZyIsImJ1aWxkRGVidWciLCJTdG9yYWdlIiwiY29uc3RydWN0b3IiLCJjb25maWciLCJ1cGxpbmtzIiwic2V0dXBVcExpbmtzIiwibG9nZ2VyIiwibG9jYWxTdG9yYWdlIiwiaW5pdCIsImZpbHRlcnMiLCJzdG9yYWdlSW5zdGFuY2UiLCJsb2FkU3RvcmFnZSIsIkxvY2FsU3RvcmFnZSIsImdldFNlY3JldCIsImFzeW5jTG9hZFBsdWdpbiIsInBsdWdpbiIsImZpbHRlcl9tZXRhZGF0YSIsInNlcnZlclNldHRpbmdzIiwicGx1Z2luUHJlZml4IiwiUExVR0lOX0NBVEVHT1JZIiwiRklMVEVSIiwibGVuZ3RoIiwibG9hZFN0b3JlUGx1Z2luIiwiXyIsImlzTmlsIiwiYXNzZXJ0Iiwic3RvcmFnZSIsIkxvY2FsRGF0YWJhc2VQbHVnaW4iLCJpbmZvIiwibmFtZSIsInBsdWdpbkNhdGVnb3J5IiwiU1RPUkFHRSIsInBsdWdpbnMiLCJzdG9yZSIsImdldFBhY2thZ2VTdG9yYWdlIiwid2FybiIsImhlYWQiLCJhZGRQYWNrYWdlIiwibWV0YWRhdGEiLCJjYWxsYmFjayIsImNoZWNrUGFja2FnZUxvY2FsIiwiY2hlY2tQYWNrYWdlUmVtb3RlIiwiX2lzQWxsb3dQdWJsaXNoT2ZmbGluZSIsIl9zeW5jVXBsaW5rc01ldGFkYXRhIiwiYmluZCIsInB1Ymxpc2hQYWNrYWdlIiwiZXJyIiwicHVibGlzaCIsImlzQm9vbGVhbiIsImFsbG93X29mZmxpbmUiLCJyZWFkVG9rZW5zIiwiZmlsdGVyIiwic2F2ZVRva2VuIiwidG9rZW4iLCJkZWxldGVUb2tlbiIsInVzZXIiLCJ0b2tlbktleSIsImFkZFZlcnNpb24iLCJ2ZXJzaW9uIiwidGFnIiwibWVyZ2VUYWdzIiwidGFnSGFzaCIsImNoYW5nZVBhY2thZ2UiLCJyZXZpc2lvbiIsInJlbW92ZVBhY2thZ2UiLCJTZWFyY2hNZW1vcnlJbmRleGVyIiwicmVtb3ZlIiwiY2F0Y2giLCJyZWFzb24iLCJlcnJvciIsInJlbW92ZVRhcmJhbGwiLCJmaWxlbmFtZSIsImFkZFRhcmJhbGwiLCJoYXNMb2NhbFRhcmJhbGwiLCJzZWxmIiwiUHJvbWlzZSIsInJlc29sdmUiLCJyZWplY3QiLCJsb2NhbFN0cmVhbSIsImdldFRhcmJhbGwiLCJpc09wZW4iLCJvbiIsInN0YXR1cyIsIkhUVFBfU1RBVFVTIiwiTk9UX0ZPVU5EIiwiYWJvcnQiLCJyZWFkU3RyZWFtIiwiUmVhZFRhcmJhbGwiLCJfaXNUYXJiYWxsQWxsb3dlZEJ5RmlsdGVycyIsInRoZW4iLCJhbGxvd2VkIiwiZW1pdCIsIkVycm9yQ29kZSIsImdldE5vdEZvdW5kIiwiQVBJX0VSUk9SIiwiTk9fUEFDS0FHRSIsImVycjQwNCIsImxvb2t1cEZyb21VcGxpbmtzIiwic3luY0VyciIsInN5bmNJbmZvIiwiaGFzVGFyYmFsbCIsImRpc3RGaWxlIiwibG9va3VwRGlzdEZpbGUiLCJ1cmwiLCJzZXJ2ZUZpbGUiLCJnZXRQYWNrYWdlTWV0YWRhdGFBc3luYyIsInYiLCJwaXBlIiwiZmlsZSIsInVwbGluayIsInJlZ2lzdHJ5IiwiY2FuZGlkYXRlcyIsInVwbGlua0lkIiwiaGFzUHJveHlUbyIsInBhY2thZ2VzIiwicHVzaCIsImNhbmRpZGF0ZSIsImlzVXBsaW5rVmFsaWQiLCJ1cG5hbWUiLCJQcm94eVN0b3JhZ2UiLCJjYWNoZSIsIl9hdXRvZ2VuZXJhdGVkIiwic2F2ZXN0cmVhbSIsIm9uX29wZW4iLCJyc3RyZWFtMiIsImZldGNoVGFyYmFsbCIsImRvbmUiLCJmaWxlTmFtZSIsImdldFBhY2thZ2UiLCJvcHRpb25zIiwiZ2V0UGFja2FnZU1ldGFkYXRhIiwiZGF0YSIsIklOVEVSTkFMX0VSUk9SIiwicmVxIiwidXBsaW5rc0xvb2siLCJnZXRQYWNrYWdlU3luVXBMaW5rc0NhbGxiYWNrIiwicmVzdWx0IiwidXBsaW5rRXJyb3JzIiwibm9ybWFsaXplRGlzdFRhZ3MiLCJjbGVhblVwTGlua3NSZWYiLCJrZWVwVXBMaW5rRGF0YSIsIl9hdHRhY2htZW50cyIsImFiYnJldmlhdGVkIiwiY29udmVydEFiYnJldmlhdGVkTWFuaWZlc3QiLCJzZWFyY2giLCJzdGFydGtleSIsInNlYXJjaFN0cmVhbSIsIlN0cmVhbSIsIlBhc3NUaHJvdWdoIiwib2JqZWN0TW9kZSIsImFzeW5jIiwiZWFjaFNlcmllcyIsIk9iamVjdCIsImtleXMiLCJ1cF9uYW1lIiwiY2IiLCJxdWVyeSIsImxvY2FsIiwidW5kZWZpbmVkIiwidXBsaW5rU3RyZWFtIiwiZW5kIiwibG9jYWxTZWFyY2hTdHJlYW0iLCJnZXRMb2NhbERhdGFiYXNlIiwic3RvcmFnZVBsdWdpbiIsImdldCIsImxvY2FscyIsIl9jb2xsZWN0TG9jYWxQYWNrYWdlcyIsInBrZ01ldGFkYXRhIiwiZmlsdGVyZWRQYWNrYWdlIiwiX2FwcGx5RmlsdGVycyIsImxhdGVzdCIsIkRJU1RfVEFHUyIsInZlcnNpb25zIiwidGltZUxpc3QiLCJ0aW1lIiwidXNlcnMiLCJwYWNrYWdlIiwicGFja2FnZUluZm8iLCJmb3VuZCIsInVwTGlua3MiLCJoYXNUb0xvb2tJbnRvVXBsaW5rcyIsImdlbmVyYXRlUGFja2FnZVRlbXBsYXRlIiwibWFwIiwidXBMaW5rIiwiX29wdGlvbnMiLCJhc3NpZ24iLCJ1cExpbmtNZXRhIiwiX3VwbGlua3MiLCJpc09iamVjdCIsImZldGNoZWQiLCJEYXRlIiwibm93IiwibWF4YWdlIiwiZXRhZyIsImdldFJlbW90ZU1ldGFkYXRhIiwidXBMaW5rUmVzcG9uc2UiLCJlVGFnIiwicmVtb3RlU3RhdHVzIiwibWVzc2FnZSIsImdldEludGVybmFsRXJyb3IiLCJ2YWxpZGF0aW9uVXRpbHMiLCJub3JtYWxpemVNZXRhZGF0YSIsInN1YiIsIm1lcmdlVXBsaW5rVGltZUludG9Mb2NhbCIsInVwZGF0ZVZlcnNpb25zSGlkZGVuVXBMaW5rIiwibWVyZ2VWZXJzaW9ucyIsInVwTGlua3NFcnJvcnMiLCJBcnJheSIsImlzQXJyYXkiLCJ1cGxpbmtUaW1lb3V0RXJyb3IiLCJpIiwiaiIsImNvZGUiLCJnZXRTZXJ2aWNlVW5hdmFpbGFibGUiLCJmaWx0ZXJFcnJvcnMiLCJwYWNrYWdlSnNvbkxvY2FsIiwidXBkYXRlVmVyc2lvbnNBc3luYyIsImNvbmNhdCIsIl91cGRhdGVWZXJzaW9uc0hpZGRlblVwTGluayIsInByb3RvdHlwZSIsImhhc093blByb3BlcnR5IiwiY2FsbCIsIlN5bWJvbCIsImZvciIsIl9kZWZhdWx0IiwiZXhwb3J0cyJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9saWIvc3RvcmFnZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgYXNzZXJ0IGZyb20gJ2Fzc2VydCc7XG5pbXBvcnQgYXN5bmMgZnJvbSAnYXN5bmMnO1xuaW1wb3J0IGJ1aWxkRGVidWcgZnJvbSAnZGVidWcnO1xuaW1wb3J0IF8gZnJvbSAnbG9kYXNoJztcbmltcG9ydCBTdHJlYW0gZnJvbSAnc3RyZWFtJztcblxuaW1wb3J0IHsgaGFzUHJveHlUbyB9IGZyb20gJ0B2ZXJkYWNjaW8vY29uZmlnJztcbmltcG9ydCB7IFBMVUdJTl9DQVRFR09SWSwgcGx1Z2luVXRpbHMsIHZhbGlkYXRpb25VdGlscyB9IGZyb20gJ0B2ZXJkYWNjaW8vY29yZSc7XG5pbXBvcnQgeyBhc3luY0xvYWRQbHVnaW4gfSBmcm9tICdAdmVyZGFjY2lvL2xvYWRlcnMnO1xuaW1wb3J0IExvY2FsRGF0YWJhc2VQbHVnaW4gZnJvbSAnQHZlcmRhY2Npby9sb2NhbC1zdG9yYWdlLWxlZ2FjeSc7XG5pbXBvcnQgeyBTZWFyY2hNZW1vcnlJbmRleGVyIH0gZnJvbSAnQHZlcmRhY2Npby9zZWFyY2gtaW5kZXhlcic7XG5pbXBvcnQgeyBSZWFkVGFyYmFsbCB9IGZyb20gJ0B2ZXJkYWNjaW8vc3RyZWFtcyc7XG5pbXBvcnQge1xuICBDYWxsYmFjayxcbiAgQ29uZmlnLFxuICBEaXN0RmlsZSxcbiAgTG9nZ2VyLFxuICBNYW5pZmVzdCxcbiAgTWVyZ2VUYWdzLFxuICBWZXJzaW9uLFxuICBWZXJzaW9ucyxcbn0gZnJvbSAnQHZlcmRhY2Npby90eXBlcyc7XG5pbXBvcnQgeyBHZW5lcmljQm9keSwgVG9rZW4sIFRva2VuRmlsdGVyIH0gZnJvbSAnQHZlcmRhY2Npby90eXBlcyc7XG5cbmltcG9ydCB7IFN0b3JhZ2VQbHVnaW5MZWdhY3kgfSBmcm9tICcuLi8uLi90eXBlcy9jdXN0b20nO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vbGliL2xvZ2dlcic7XG5pbXBvcnQgeyBJUGx1Z2luRmlsdGVycywgSVN5bmNVcGxpbmtzLCBTdHJpbmdWYWx1ZSB9IGZyb20gJy4uL3R5cGVzJztcbmltcG9ydCB7IEFQSV9FUlJPUiwgRElTVF9UQUdTLCBIVFRQX1NUQVRVUyB9IGZyb20gJy4vY29uc3RhbnRzJztcbmltcG9ydCBMb2NhbFN0b3JhZ2UsIHsgU3RvcmFnZVBsdWdpbiB9IGZyb20gJy4vbG9jYWwtc3RvcmFnZSc7XG5pbXBvcnQgeyBtZXJnZVZlcnNpb25zIH0gZnJvbSAnLi9tZXRhZGF0YS11dGlscyc7XG5pbXBvcnQge1xuICBjaGVja1BhY2thZ2VMb2NhbCxcbiAgY2hlY2tQYWNrYWdlUmVtb3RlLFxuICBjbGVhblVwTGlua3NSZWYsXG4gIGNvbnZlcnRBYmJyZXZpYXRlZE1hbmlmZXN0LFxuICBnZW5lcmF0ZVBhY2thZ2VUZW1wbGF0ZSxcbiAgbG9va3VwRGlzdEZpbGUsXG4gIG1lcmdlVXBsaW5rVGltZUludG9Mb2NhbCxcbiAgcHVibGlzaFBhY2thZ2UsXG59IGZyb20gJy4vc3RvcmFnZS11dGlscyc7XG5pbXBvcnQgUHJveHlTdG9yYWdlIGZyb20gJy4vdXAtc3RvcmFnZSc7XG5pbXBvcnQgeyBzZXR1cFVwTGlua3MsIHVwZGF0ZVZlcnNpb25zSGlkZGVuVXBMaW5rIH0gZnJvbSAnLi91cGxpbmstdXRpbCc7XG5pbXBvcnQgeyBFcnJvckNvZGUsIGhhc1RhcmJhbGwsIGlzT2JqZWN0LCBub3JtYWxpemVEaXN0VGFncyB9IGZyb20gJy4vdXRpbHMnO1xuXG5jb25zdCBkZWJ1ZyA9IGJ1aWxkRGVidWcoJ3ZlcmRhY2NpbzpzdG9yYWdlJyk7XG5cbmNsYXNzIFN0b3JhZ2Uge1xuICBwdWJsaWMgbG9jYWxTdG9yYWdlOiBMb2NhbFN0b3JhZ2U7XG4gIHB1YmxpYyBjb25maWc6IENvbmZpZztcbiAgcHVibGljIGxvZ2dlcjogTG9nZ2VyO1xuICBwdWJsaWMgdXBsaW5rczogUmVjb3JkPHN0cmluZywgUHJveHlTdG9yYWdlPjtcbiAgcHVibGljIGZpbHRlcnM6IElQbHVnaW5GaWx0ZXJzIHwgdW5kZWZpbmVkO1xuXG4gIHB1YmxpYyBjb25zdHJ1Y3Rvcihjb25maWc6IENvbmZpZykge1xuICAgIHRoaXMuY29uZmlnID0gY29uZmlnO1xuICAgIHRoaXMudXBsaW5rcyA9IHNldHVwVXBMaW5rcyhjb25maWcpO1xuICAgIHRoaXMubG9nZ2VyID0gbG9nZ2VyO1xuICAgIC8vIEB0cy1pZ25vcmVcbiAgICB0aGlzLmxvY2FsU3RvcmFnZSA9IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogSW5pdGlhbGl6ZSB0aGUgc3RvcmFnZTogbG9hZCB0aGUgc3RvcmFnZSBwbHVnaW4gKG9yIHRoZSBkZWZhdWx0XG4gICAqIGxvY2FsIHN0b3JhZ2UpIGFuZCB0aGUgZmlsdGVyIHBsdWdpbnMuIFNhZmUgdG8gY2FsbCBvbmNlOyBzdWJzZXF1ZW50XG4gICAqIGNhbGxzIG9ubHkgcmVsb2FkIG1pc3NpbmcgZmlsdGVycy5cbiAgICogQHBhcmFtIHtDb25maWd9IGNvbmZpZyB2ZXJkYWNjaW8gY29uZmlndXJhdGlvblxuICAgKiBAcGFyYW0ge0lQbHVnaW5GaWx0ZXJzfSBmaWx0ZXJzIHByZWxvYWRlZCBmaWx0ZXIgcGx1Z2luczsgd2hlbiBvbWl0dGVkIHRoZXkgYXJlIGxvYWRlZCBmcm9tIHRoZSBjb25maWd1cmF0aW9uXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgaW5pdChjb25maWc6IENvbmZpZywgZmlsdGVycz86IElQbHVnaW5GaWx0ZXJzKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMubG9jYWxTdG9yYWdlID09PSBudWxsKSB7XG4gICAgICB0aGlzLmZpbHRlcnMgPSBmaWx0ZXJzO1xuICAgICAgY29uc3Qgc3RvcmFnZUluc3RhbmNlID0gYXdhaXQgdGhpcy5sb2FkU3RvcmFnZShjb25maWcsIHRoaXMubG9nZ2VyKTtcbiAgICAgIHRoaXMubG9jYWxTdG9yYWdlID0gbmV3IExvY2FsU3RvcmFnZSh0aGlzLmNvbmZpZywgbG9nZ2VyLCBzdG9yYWdlSW5zdGFuY2UpO1xuICAgICAgYXdhaXQgdGhpcy5sb2NhbFN0b3JhZ2UuZ2V0U2VjcmV0KGNvbmZpZyk7XG4gICAgICBkZWJ1ZygnaW5pdGlhbGl6YXRpb24gY29tcGxldGVkJyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGRlYnVnKCdzdG9yYWdlIGhhcyBiZWVuIGFscmVhZHkgaW5pdGlhbGl6ZWQnKTtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMuZmlsdGVycykge1xuICAgICAgdGhpcy5maWx0ZXJzID0gYXdhaXQgYXN5bmNMb2FkUGx1Z2luPHBsdWdpblV0aWxzLk1hbmlmZXN0RmlsdGVyPHVua25vd24+PihcbiAgICAgICAgdGhpcy5jb25maWcuZmlsdGVycyxcbiAgICAgICAge1xuICAgICAgICAgIGNvbmZpZzogdGhpcy5jb25maWcsXG4gICAgICAgICAgbG9nZ2VyOiB0aGlzLmxvZ2dlcixcbiAgICAgICAgfSxcbiAgICAgICAgKHBsdWdpbjogcGx1Z2luVXRpbHMuTWFuaWZlc3RGaWx0ZXI8Q29uZmlnPikgPT4ge1xuICAgICAgICAgIHJldHVybiB0eXBlb2YgcGx1Z2luLmZpbHRlcl9tZXRhZGF0YSAhPT0gJ3VuZGVmaW5lZCc7XG4gICAgICAgIH0sXG4gICAgICAgIHRydWUsXG4gICAgICAgIHRoaXMuY29uZmlnPy5zZXJ2ZXJTZXR0aW5ncz8ucGx1Z2luUHJlZml4LFxuICAgICAgICBQTFVHSU5fQ0FURUdPUlkuRklMVEVSXG4gICAgICApO1xuICAgICAgZGVidWcoJ2ZpbHRlcnMgYXZhaWxhYmxlICVvJywgdGhpcy5maWx0ZXJzLmxlbmd0aCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJlc29sdmUgdGhlIHN0b3JhZ2UgYmFja2VuZDogYSBjb25maWd1cmVkIHN0b3JhZ2UgcGx1Z2luIHdoZW4gYXZhaWxhYmxlLFxuICAgKiBvdGhlcndpc2UgdGhlIGRlZmF1bHQgYEB2ZXJkYWNjaW8vbG9jYWwtc3RvcmFnZWAgb24gdGhlIGNvbmZpZ3VyZWQgcGF0aC5cbiAgICogQHJldHVybiB7UHJvbWlzZTxTdG9yYWdlUGx1Z2luPn0gdGhlIHN0b3JhZ2UgcGx1Z2luIGluc3RhbmNlXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGxvYWRTdG9yYWdlKGNvbmZpZzogQ29uZmlnLCBsb2dnZXI6IExvZ2dlcik6IFByb21pc2U8U3RvcmFnZVBsdWdpbj4ge1xuICAgIGNvbnN0IFN0b3JhZ2UgPSBhd2FpdCB0aGlzLmxvYWRTdG9yZVBsdWdpbigpO1xuICAgIGlmIChfLmlzTmlsKFN0b3JhZ2UpKSB7XG4gICAgICBhc3NlcnQodGhpcy5jb25maWcuc3RvcmFnZSwgJ0NPTkZJRzogc3RvcmFnZSBwYXRoIG5vdCBkZWZpbmVkJyk7XG4gICAgICBkZWJ1Zygnbm8gY3VzdG9tIHN0b3JhZ2UgZm91bmQsIGxvYWRpbmcgZGVmYXVsdCBzdG9yYWdlIEB2ZXJkYWNjaW8vbG9jYWwtc3RvcmFnZScpO1xuICAgICAgY29uc3QgbG9jYWxTdG9yYWdlID0gbmV3IExvY2FsRGF0YWJhc2VQbHVnaW4oY29uZmlnLCBsb2dnZXIpO1xuICAgICAgbG9nZ2VyLmluZm8oXG4gICAgICAgIHsgbmFtZTogJ0B2ZXJkYWNjaW8vbG9jYWwtc3RvcmFnZScsIHBsdWdpbkNhdGVnb3J5OiBQTFVHSU5fQ0FURUdPUlkuU1RPUkFHRSB9LFxuICAgICAgICAncGx1Z2luIEB7bmFtZX0gc3VjY2Vzc2Z1bGx5IGxvYWRlZCAoQHtwbHVnaW5DYXRlZ29yeX0pJ1xuICAgICAgKTtcbiAgICAgIHJldHVybiBsb2NhbFN0b3JhZ2U7XG4gICAgfVxuICAgIHJldHVybiBTdG9yYWdlIGFzIFN0b3JhZ2VQbHVnaW47XG4gIH1cblxuICAvKipcbiAgICogTG9hZCB0aGUgc3RvcmFnZSBwbHVnaW5zIGRlY2xhcmVkIHVuZGVyIGBzdG9yZWAgaW4gdGhlIGNvbmZpZ3VyYXRpb24uXG4gICAqIE9ubHkgb25lIHN0b3JhZ2UgaXMgc3VwcG9ydGVkOiB3aXRoIHNldmVyYWwgcGx1Z2lucyBjb25maWd1cmVkIHRoZVxuICAgKiBmaXJzdCBsb2FkZWQgb25lIHdpbnMgYW5kIGEgd2FybmluZyBpcyBsb2dnZWQuXG4gICAqIEByZXR1cm4ge1Byb21pc2U8U3RvcmFnZVBsdWdpbkxlZ2FjeTxDb25maWc+IHwgdW5kZWZpbmVkPn0gdGhlIHNlbGVjdGVkIHBsdWdpbiwgb3IgdW5kZWZpbmVkIHdoZW4gbm9uZSBpcyBjb25maWd1cmVkXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGxvYWRTdG9yZVBsdWdpbigpOiBQcm9taXNlPFN0b3JhZ2VQbHVnaW5MZWdhY3k8Q29uZmlnPiB8IHVuZGVmaW5lZD4ge1xuICAgIGNvbnN0IHBsdWdpbnM6IFN0b3JhZ2VQbHVnaW5MZWdhY3k8Q29uZmlnPltdID0gYXdhaXQgYXN5bmNMb2FkUGx1Z2luPFxuICAgICAgcGx1Z2luVXRpbHMuU3RvcmFnZTx1bmtub3duPlxuICAgID4oXG4gICAgICB0aGlzLmNvbmZpZy5zdG9yZSxcbiAgICAgIHtcbiAgICAgICAgY29uZmlnOiB0aGlzLmNvbmZpZyxcbiAgICAgICAgbG9nZ2VyOiB0aGlzLmxvZ2dlcixcbiAgICAgIH0sXG4gICAgICAocGx1Z2luKSA9PiB7XG4gICAgICAgIHJldHVybiB0eXBlb2YgcGx1Z2luLmdldFBhY2thZ2VTdG9yYWdlICE9PSAndW5kZWZpbmVkJztcbiAgICAgIH0sXG4gICAgICB0cnVlLFxuICAgICAgdGhpcy5jb25maWc/LnNlcnZlclNldHRpbmdzPy5wbHVnaW5QcmVmaXgsXG4gICAgICBQTFVHSU5fQ0FURUdPUlkuU1RPUkFHRVxuICAgICk7XG5cbiAgICBpZiAocGx1Z2lucy5sZW5ndGggPiAxKSB7XG4gICAgICB0aGlzLmxvZ2dlci53YXJuKFxuICAgICAgICAnbW9yZSB0aGFuIG9uZSBzdG9yYWdlIHBsdWdpbnMgaGFzIGJlZW4gZGV0ZWN0ZWQsIG11bHRpcGxlIHN0b3JhZ2UgYXJlIG5vdCBzdXBwb3J0ZWQsIG9uZSB3aWxsIGJlIHNlbGVjdGVkIGF1dG9tYXRpY2FsbHknXG4gICAgICApO1xuICAgIH1cblxuICAgIHJldHVybiBfLmhlYWQocGx1Z2lucyk7XG4gIH1cblxuICAvKipcbiAgICogQWRkIGEgcGFja2FnZSB0byB0aGUgc3lzdGVtIChwdWJsaXNoIGZsb3cpLlxuICAgKiBWZXJpZmllcyB0aGUgcGFja2FnZSBkb2VzIG5vdCBleGlzdCBsb2NhbGx5IG5vciBvbiBhbnkgdXBsaW5rIGJlZm9yZVxuICAgKiBjcmVhdGluZyBpdCBsb2NhbGx5OyB1cGxpbmtzIGJlaW5nIG9mZmxpbmUgcmVqZWN0cyB0aGUgcHVibGlzaCB1bmxlc3NcbiAgICogYHB1Ymxpc2guYWxsb3dfb2ZmbGluZWAgaXMgZW5hYmxlZC5cbiAgICogVXNlZCBzdG9yYWdlczogbG9jYWwgKHdyaXRlKSAmJiB1cGxpbmtzXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBuYW1lIHBhY2thZ2UgbmFtZVxuICAgKiBAcGFyYW0ge09iamVjdH0gbWV0YWRhdGEgcGFja2FnZSBtYW5pZmVzdCB0byBwdWJsaXNoXG4gICAqIEBwYXJhbSB7RnVuY3Rpb259IGNhbGxiYWNrIGludm9rZWQgd2l0aCBhbiBlcnJvciB3aGVuIHRoZSBwdWJsaXNoIGlzIHJlamVjdGVkXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgYWRkUGFja2FnZShuYW1lOiBzdHJpbmcsIG1ldGFkYXRhOiBhbnksIGNhbGxiYWNrOiBhbnkpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBkZWJ1ZygnYWRkIHBhY2thZ2UgJW8nLCBuYW1lKTtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgY2hlY2tQYWNrYWdlTG9jYWwobmFtZSwgdGhpcy5sb2NhbFN0b3JhZ2UpO1xuICAgICAgYXdhaXQgY2hlY2tQYWNrYWdlUmVtb3RlKFxuICAgICAgICBuYW1lLFxuICAgICAgICB0aGlzLl9pc0FsbG93UHVibGlzaE9mZmxpbmUoKSxcbiAgICAgICAgdGhpcy5fc3luY1VwbGlua3NNZXRhZGF0YS5iaW5kKHRoaXMpXG4gICAgICApO1xuICAgICAgYXdhaXQgcHVibGlzaFBhY2thZ2UobmFtZSwgbWV0YWRhdGEsIHRoaXMubG9jYWxTdG9yYWdlKTtcbiAgICAgIGNhbGxiYWNrKCk7XG4gICAgfSBjYXRjaCAoZXJyOiBhbnkpIHtcbiAgICAgIGNhbGxiYWNrKGVycik7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgYHB1Ymxpc2guYWxsb3dfb2ZmbGluZWAgaXMgZW5hYmxlZCBpbiB0aGUgY29uZmlndXJhdGlvbi5cbiAgICogQHJldHVybiB7Qm9vbGVhbn0gdHJ1ZSB3aGVuIHB1Ymxpc2hpbmcgd2l0aCB1bnJlYWNoYWJsZSB1cGxpbmtzIGlzIGFsbG93ZWRcbiAgICovXG4gIHByaXZhdGUgX2lzQWxsb3dQdWJsaXNoT2ZmbGluZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgdHlwZW9mIHRoaXMuY29uZmlnLnB1Ymxpc2ggIT09ICd1bmRlZmluZWQnICYmXG4gICAgICBfLmlzQm9vbGVhbih0aGlzLmNvbmZpZy5wdWJsaXNoLmFsbG93X29mZmxpbmUpICYmXG4gICAgICB0aGlzLmNvbmZpZy5wdWJsaXNoLmFsbG93X29mZmxpbmVcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlYWQgdGhlIG5wbSB0b2tlbnMgbWF0Y2hpbmcgdGhlIGZpbHRlciBmcm9tIHRoZSB0b2tlbiBzdG9yYWdlLlxuICAgKiBVc2VkIHN0b3JhZ2VzOiBsb2NhbCAocmVhZClcbiAgICovXG4gIHB1YmxpYyByZWFkVG9rZW5zKGZpbHRlcjogVG9rZW5GaWx0ZXIpOiBQcm9taXNlPFRva2VuW10+IHtcbiAgICByZXR1cm4gdGhpcy5sb2NhbFN0b3JhZ2UucmVhZFRva2VucyhmaWx0ZXIpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNhdmUgYW4gbnBtIHRva2VuIHRvIHRoZSB0b2tlbiBzdG9yYWdlLlxuICAgKiBVc2VkIHN0b3JhZ2VzOiBsb2NhbCAod3JpdGUpXG4gICAqL1xuICBwdWJsaWMgc2F2ZVRva2VuKHRva2VuOiBUb2tlbik6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmxvY2FsU3RvcmFnZS5zYXZlVG9rZW4odG9rZW4pO1xuICB9XG5cbiAgLyoqXG4gICAqIERlbGV0ZSBhbiBucG0gdG9rZW4gZnJvbSB0aGUgdG9rZW4gc3RvcmFnZS5cbiAgICogVXNlZCBzdG9yYWdlczogbG9jYWwgKHdyaXRlKVxuICAgKi9cbiAgcHVibGljIGRlbGV0ZVRva2VuKHVzZXI6IHN0cmluZywgdG9rZW5LZXk6IHN0cmluZyk6IFByb21pc2U8YW55PiB7XG4gICAgcmV0dXJuIHRoaXMubG9jYWxTdG9yYWdlLmRlbGV0ZVRva2VuKHVzZXIsIHRva2VuS2V5KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGQgYSBuZXcgdmVyc2lvbiBvZiBhIHBhY2thZ2UgdG8gdGhlIHN5c3RlbS5cbiAgICogVXNlZCBzdG9yYWdlczogbG9jYWwgKHdyaXRlKVxuICAgKiBAcGFyYW0ge1N0cmluZ30gbmFtZSBwYWNrYWdlIG5hbWVcbiAgICogQHBhcmFtIHtTdHJpbmd9IHZlcnNpb24gdmVyc2lvbiBpZCwgZWcuIDEuMC4wXG4gICAqIEBwYXJhbSB7VmVyc2lvbn0gbWV0YWRhdGEgdmVyc2lvbiBtZXRhZGF0YVxuICAgKiBAcGFyYW0ge1N0cmluZ30gdGFnIGRpc3QtdGFnIHBvaW50aW5nIHRvIHRoZSB2ZXJzaW9uXG4gICAqIEBwYXJhbSB7RnVuY3Rpb259IGNhbGxiYWNrXG4gICAqL1xuICBwdWJsaWMgYWRkVmVyc2lvbihcbiAgICBuYW1lOiBzdHJpbmcsXG4gICAgdmVyc2lvbjogc3RyaW5nLFxuICAgIG1ldGFkYXRhOiBWZXJzaW9uLFxuICAgIHRhZzogU3RyaW5nVmFsdWUsXG4gICAgY2FsbGJhY2s6IENhbGxiYWNrXG4gICk6IHZvaWQge1xuICAgIGRlYnVnKCdhZGQgdmVyc2lvbiAlcyBwYWNrYWdlIGZvciAlcycsIHZlcnNpb24sIG5hbWUpO1xuICAgIHRoaXMubG9jYWxTdG9yYWdlLmFkZFZlcnNpb24obmFtZSwgdmVyc2lvbiwgbWV0YWRhdGEsIHRhZywgY2FsbGJhY2spO1xuICB9XG5cbiAgLyoqXG4gICAqIFRhZyBwYWNrYWdlIHZlcnNpb25zIHdpdGggdGhlIHByb3ZpZGVkIGRpc3QtdGFncy5cbiAgICogVXNlZCBzdG9yYWdlczogbG9jYWwgKHdyaXRlKVxuICAgKiBAcGFyYW0ge1N0cmluZ30gbmFtZSBwYWNrYWdlIG5hbWVcbiAgICogQHBhcmFtIHtNZXJnZVRhZ3N9IHRhZ0hhc2ggZGlzdC10YWcgdG8gdmVyc2lvbiBtYXBwaW5nIHRvIG1lcmdlXG4gICAqIEBwYXJhbSB7RnVuY3Rpb259IGNhbGxiYWNrXG4gICAqL1xuICBwdWJsaWMgbWVyZ2VUYWdzKG5hbWU6IHN0cmluZywgdGFnSGFzaDogTWVyZ2VUYWdzLCBjYWxsYmFjazogQ2FsbGJhY2spOiB2b2lkIHtcbiAgICBkZWJ1ZygnbWVyZ2UgdGFncyBmb3IgJW8nLCBuYW1lKTtcbiAgICB0aGlzLmxvY2FsU3RvcmFnZS5tZXJnZVRhZ3MobmFtZSwgdGFnSGFzaCwgY2FsbGJhY2spO1xuICB9XG5cbiAgLyoqXG4gICAqIENoYW5nZSBhbiBleGlzdGluZyBwYWNrYWdlIChlZy4gdW5wdWJsaXNoIG9uZSB2ZXJzaW9uKS5cbiAgICogVXNlZCBzdG9yYWdlczogbG9jYWwgKHdyaXRlKVxuICAgKiBAcGFyYW0ge1N0cmluZ30gbmFtZSBwYWNrYWdlIG5hbWVcbiAgICogQHBhcmFtIHtNYW5pZmVzdH0gbWV0YWRhdGEgdXBkYXRlZCBwYWNrYWdlIG1hbmlmZXN0XG4gICAqIEBwYXJhbSB7U3RyaW5nfSByZXZpc2lvbiBleHBlY3RlZCByZXZpc2lvbiBvZiB0aGUgc3RvcmVkIG1hbmlmZXN0XG4gICAqIEBwYXJhbSB7RnVuY3Rpb259IGNhbGxiYWNrXG4gICAqL1xuICBwdWJsaWMgY2hhbmdlUGFja2FnZShcbiAgICBuYW1lOiBzdHJpbmcsXG4gICAgbWV0YWRhdGE6IE1hbmlmZXN0LFxuICAgIHJldmlzaW9uOiBzdHJpbmcsXG4gICAgY2FsbGJhY2s6IENhbGxiYWNrXG4gICk6IHZvaWQge1xuICAgIGRlYnVnKCdjaGFuZ2UgZXhpc3RpbmcgcGFja2FnZSBmb3IgcGFja2FnZSAlbyByZXZpc2lvbiAlbycsIG5hbWUsIHJldmlzaW9uKTtcbiAgICB0aGlzLmxvY2FsU3RvcmFnZS5jaGFuZ2VQYWNrYWdlKG5hbWUsIG1ldGFkYXRhLCByZXZpc2lvbiwgY2FsbGJhY2spO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZSBhIHBhY2thZ2UgZnJvbSB0aGUgbG9jYWwgc3RvcmFnZSBhbmQgdGhlIHNlYXJjaCBpbmRleGVyLlxuICAgKiBVc2VkIHN0b3JhZ2VzOiBsb2NhbCAod3JpdGUpXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBuYW1lIHBhY2thZ2UgbmFtZVxuICAgKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFja1xuICAgKi9cbiAgcHVibGljIHJlbW92ZVBhY2thZ2UobmFtZTogc3RyaW5nLCBjYWxsYmFjazogQ2FsbGJhY2spOiB2b2lkIHtcbiAgICBkZWJ1ZygncmVtb3ZlIHBhY2thZ2UgJW8nLCBuYW1lKTtcbiAgICB0aGlzLmxvY2FsU3RvcmFnZS5yZW1vdmVQYWNrYWdlKG5hbWUsIGNhbGxiYWNrKTtcbiAgICAvLyB1cGRhdGUgdGhlIGluZGV4ZXJcbiAgICBTZWFyY2hNZW1vcnlJbmRleGVyLnJlbW92ZShuYW1lKS5jYXRjaCgocmVhc29uKSA9PiB7XG4gICAgICBkZWJ1ZygnaW5kZXhlciBoYXMgZmFpbGVkIG9uIHJlbW92ZSBpdGVtICVvJywgcmVhc29uKTtcbiAgICAgIGxvZ2dlci5lcnJvcignaW5kZXhlciBoYXMgZmFpbGVkIG9uIHJlbW92ZSBpdGVtJyk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlIGEgdGFyYmFsbCBmcm9tIHRoZSBsb2NhbCBzdG9yYWdlLiBUaGUgdGFyYmFsbCBtdXN0IG5vdCBiZVxuICAgKiBsaW5rZWQgYnkgYW55IGV4aXN0aW5nIHZlcnNpb24sIGllLiB0aGUgdmVyc2lvbiBzaG91bGQgYmVcbiAgICogdW5wdWJsaXNoZWQgZmlyc3QuXG4gICAqIFVzZWQgc3RvcmFnZXM6IGxvY2FsICh3cml0ZSlcbiAgICogQHBhcmFtIHtTdHJpbmd9IG5hbWUgcGFja2FnZSBuYW1lXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBmaWxlbmFtZSB0YXJiYWxsIGZpbGUgbmFtZVxuICAgKiBAcGFyYW0ge1N0cmluZ30gcmV2aXNpb24gZXhwZWN0ZWQgcmV2aXNpb24gb2YgdGhlIHN0b3JlZCBtYW5pZmVzdFxuICAgKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFja1xuICAgKi9cbiAgcHVibGljIHJlbW92ZVRhcmJhbGwobmFtZTogc3RyaW5nLCBmaWxlbmFtZTogc3RyaW5nLCByZXZpc2lvbjogc3RyaW5nLCBjYWxsYmFjazogQ2FsbGJhY2spOiB2b2lkIHtcbiAgICBkZWJ1ZygncmVtb3ZlIHRhcmJhbGwgJXMgZm9yICVzJywgZmlsZW5hbWUsIG5hbWUpO1xuICAgIHRoaXMubG9jYWxTdG9yYWdlLnJlbW92ZVRhcmJhbGwobmFtZSwgZmlsZW5hbWUsIHJldmlzaW9uLCBjYWxsYmFjayk7XG4gIH1cblxuICAvKipcbiAgICogVXBsb2FkIGEgdGFyYmFsbCAocHVibGlzaCBmbG93KS4gU3luY2hyb25vdXMsIHJldHVybnMgYSB3cml0YWJsZVxuICAgKiBzdHJlYW0gdGhlIHRhcmJhbGwgYm9keSBpcyBwaXBlZCBpbnRvLlxuICAgKiBVc2VkIHN0b3JhZ2VzOiBsb2NhbCAod3JpdGUpXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBuYW1lIHBhY2thZ2UgbmFtZVxuICAgKiBAcGFyYW0ge1N0cmluZ30gZmlsZW5hbWUgdGFyYmFsbCBmaWxlIG5hbWVcbiAgICogQHJldHVybiB7U3RyZWFtfSB3cml0YWJsZSB1cGxvYWQgc3RyZWFtXG4gICAqL1xuICBwdWJsaWMgYWRkVGFyYmFsbChuYW1lOiBzdHJpbmcsIGZpbGVuYW1lOiBzdHJpbmcpIHtcbiAgICBkZWJ1ZygnYWRkIGEgd