UNPKG

cnpmcore

Version:
257 lines 18.8 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PackageSearchService = void 0; const tegg_1 = require("@eggjs/tegg"); const elasticsearch_1 = require("@elastic/elasticsearch"); const dayjs_1 = __importDefault(require("dayjs")); const AbstractService_1 = require("../../common/AbstractService"); const PackageUtil_1 = require("../../common/PackageUtil"); const PackageManagerService_1 = require("./PackageManagerService"); const SearchRepository_1 = require("../../repository/SearchRepository"); const PackageVersionDownloadRepository_1 = require("../../repository/PackageVersionDownloadRepository"); const PackageRepository_1 = require("../../repository/PackageRepository"); const PackageVersionBlockRepository_1 = require("../../repository/PackageVersionBlockRepository"); let PackageSearchService = class PackageSearchService extends AbstractService_1.AbstractService { async syncPackage(fullname, isSync = true) { const [scope, name] = (0, PackageUtil_1.getScopeAndName)(fullname); const fullManifests = await this.packageManagerService.listPackageFullManifests(scope, name, isSync); if (!fullManifests.data) { this.logger.warn('[PackageSearchService.syncPackage] save package:%s not found', fullname); return; } const pkg = await this.packageRepository.findPackage(scope, name); if (!pkg) { this.logger.warn('[PackageSearchService.syncPackage] findPackage:%s not found', fullname); return; } const block = await this.packageVersionBlockRepository.findPackageBlock(pkg.packageId); if (block) { this.logger.warn('[PackageSearchService.syncPackage] package:%s is blocked, try to remove es', fullname); await this.removePackage(fullname); return; } // get last year download data const startDate = (0, dayjs_1.default)().subtract(1, 'year'); const endDate = (0, dayjs_1.default)(); const entities = await this.packageVersionDownloadRepository.query(pkg.packageId, startDate.toDate(), endDate.toDate()); let downloadsAll = 0; for (const entity of entities) { for (let i = 1; i <= 31; i++) { const day = String(i).padStart(2, '0'); const field = `d${day}`; const counter = entity[field]; if (!counter) continue; downloadsAll += counter; } } const { data: manifest } = fullManifests; const latestVersion = manifest['dist-tags'].latest; const latestManifest = manifest.versions[latestVersion]; const packageDoc = { name: manifest.name, version: latestVersion, _rev: manifest._rev, scope: scope ? scope.replace('@', '') : 'unscoped', keywords: manifest.keywords || [], versions: Object.keys(manifest.versions), description: manifest.description, license: typeof manifest.license === 'object' ? manifest.license?.type : manifest.license, maintainers: manifest.maintainers, author: (0, PackageUtil_1.formatAuthor)(manifest.author), 'dist-tags': manifest['dist-tags'], date: manifest.time[latestVersion], created: manifest.time.created, modified: manifest.time.modified, // 归属 registry,keywords 枚举值 _source_registry_name: manifest._source_registry_name, // 最新版本发布人 _npmUser: _npmUser: latestManifest?._npmUser, // 最新版本发布信息 publish_time: latestManifest?.publish_time, }; // http://npmmirror.com/package/npm/files/lib/utils/format-search-stream.js#L147-L148 // npm cli 使用 username 字段 if (packageDoc.maintainers) { packageDoc.maintainers = packageDoc.maintainers.map(maintainer => { return { username: maintainer.name, ...maintainer, }; }); } const document = { package: packageDoc, downloads: { all: downloadsAll, }, }; return await this.searchRepository.upsertPackage(document); } async searchPackage(text, from, size) { const matchQueries = this._buildMatchQueries(text); const scriptScore = this._buildScriptScore({ text, scoreEffect: 0.25, }); const res = await this.searchRepository.searchPackage({ body: { size, from, query: { function_score: { boost_mode: 'replace', query: { bool: { should: matchQueries, minimum_should_match: matchQueries.length ? 1 : 0, }, }, script_score: scriptScore, }, }, }, }); const { hits, total } = res; return { objects: hits?.map(item => { return item._source; }), total: total.value, }; } async removePackage(fullname) { try { return await this.searchRepository.removePackage(fullname); } catch (error) { // if the package does not exist, returns success if (error instanceof elasticsearch_1.errors.ResponseError && error?.statusCode === 404) { this.logger.warn('[PackageSearchService.removePackage] remove package:%s not found', fullname); return fullname; } throw error; } } // https://github.com/npms-io/queries/blob/master/lib/search.js#L8C1-L78C2 _buildMatchQueries(text) { if (!text) { return []; } return [ // Standard match using cross_fields { multi_match: { query: text, operator: 'and', fields: [ 'package.name.standard^4', 'package.description.standard', 'package.keywords.standard^2', ], type: 'cross_fields', boost: 6, tie_breaker: 0.5, }, }, // Partial match using edge-ngram { multi_match: { query: text, operator: 'and', fields: [ 'package.name.edge_ngram^4', 'package.description.edge_ngram', 'package.keywords.edge_ngram^2', ], type: 'phrase', slop: 3, boost: 3, tie_breaker: 0.5, }, }, // Normal term match with an english stemmer { multi_match: { query: text, operator: 'and', fields: [ 'package.name.english_docs^4', 'package.description.english_docs', 'package.keywords.english_docs^2', ], type: 'cross_fields', boost: 3, tie_breaker: 0.5, }, }, // Normal term match with a more aggressive english stemmer (not so important) { multi_match: { query: text, operator: 'and', fields: [ 'package.name.english_aggressive_docs^4', 'package.description.english_aggressive_docs', 'package.keywords.english_aggressive_docs^2', ], type: 'cross_fields', tie_breaker: 0.5, }, }, ]; } _buildScriptScore(params) { // keep search simple, only download(popularity) const downloads = 'doc["downloads.all"].value'; const source = `doc["package.name.raw"].value.equals(params.text) ? 100000 + ${downloads} : _score * Math.pow(${downloads}, params.scoreEffect)`; return { script: { source, params: { text: params.text || '', scoreEffect: params.scoreEffect, }, }, }; } }; exports.PackageSearchService = PackageSearchService; __decorate([ (0, tegg_1.Inject)(), __metadata("design:type", PackageManagerService_1.PackageManagerService) ], PackageSearchService.prototype, "packageManagerService", void 0); __decorate([ (0, tegg_1.Inject)(), __metadata("design:type", SearchRepository_1.SearchRepository) ], PackageSearchService.prototype, "searchRepository", void 0); __decorate([ (0, tegg_1.Inject)(), __metadata("design:type", PackageVersionDownloadRepository_1.PackageVersionDownloadRepository) ], PackageSearchService.prototype, "packageVersionDownloadRepository", void 0); __decorate([ (0, tegg_1.Inject)(), __metadata("design:type", PackageRepository_1.PackageRepository) ], PackageSearchService.prototype, "packageRepository", void 0); __decorate([ (0, tegg_1.Inject)(), __metadata("design:type", PackageVersionBlockRepository_1.PackageVersionBlockRepository) ], PackageSearchService.prototype, "packageVersionBlockRepository", void 0); exports.PackageSearchService = PackageSearchService = __decorate([ (0, tegg_1.SingletonProto)({ accessLevel: tegg_1.AccessLevel.PUBLIC, }) ], PackageSearchService); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGFja2FnZVNlYXJjaFNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9hcHAvY29yZS9zZXJ2aWNlL1BhY2thZ2VTZWFyY2hTZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7OztBQUFBLHNDQUFrRTtBQUNsRSwwREFBeUQ7QUFDekQsa0RBQTBCO0FBRTFCLGtFQUErRDtBQUMvRCwwREFBeUU7QUFDekUsbUVBQWdFO0FBQ2hFLHdFQUE0RztBQUM1Ryx3R0FBcUc7QUFDckcsMEVBQXVFO0FBQ3ZFLGtHQUErRjtBQU14RixJQUFNLG9CQUFvQixHQUExQixNQUFNLG9CQUFxQixTQUFRLGlDQUFlO0lBWXZELEtBQUssQ0FBQyxXQUFXLENBQUMsUUFBZ0IsRUFBRSxNQUFNLEdBQUcsSUFBSTtRQUMvQyxNQUFNLENBQUUsS0FBSyxFQUFFLElBQUksQ0FBRSxHQUFHLElBQUEsNkJBQWUsRUFBQyxRQUFRLENBQUMsQ0FBQztRQUNsRCxNQUFNLGFBQWEsR0FBRyxNQUFNLElBQUksQ0FBQyxxQkFBcUIsQ0FBQyx3QkFBd0IsQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBRXJHLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxFQUFFO1lBQ3ZCLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLDhEQUE4RCxFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQzNGLE9BQU87U0FDUjtRQUVELE1BQU0sR0FBRyxHQUFHLE1BQU0sSUFBSSxDQUFDLGlCQUFpQixDQUFDLFdBQVcsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDbEUsSUFBSSxDQUFDLEdBQUcsRUFBRTtZQUNSLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLDZEQUE2RCxFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQzFGLE9BQU87U0FDUjtRQUVELE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLDZCQUE2QixDQUFDLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUN2RixJQUFJLEtBQUssRUFBRTtZQUNULElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLDRFQUE0RSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQ3pHLE1BQU0sSUFBSSxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUNuQyxPQUFPO1NBQ1I7UUFFRCw4QkFBOEI7UUFDOUIsTUFBTSxTQUFTLEdBQUcsSUFBQSxlQUFLLEdBQUUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQzlDLE1BQU0sT0FBTyxHQUFHLElBQUEsZUFBSyxHQUFFLENBQUM7UUFFeEIsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsZ0NBQWdDLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDLE1BQU0sRUFBRSxFQUFFLE9BQU8sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQ3hILElBQUksWUFBWSxHQUFHLENBQUMsQ0FBQztRQUNyQixLQUFLLE1BQU0sTUFBTSxJQUFJLFFBQVEsRUFBRTtZQUM3QixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsRUFBRSxFQUFFO2dCQUM1QixNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQztnQkFDdkMsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLEVBQUUsQ0FBQztnQkFDeEIsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUM5QixJQUFJLENBQUMsT0FBTztvQkFBRSxTQUFTO2dCQUN2QixZQUFZLElBQUksT0FBTyxDQUFDO2FBQ3pCO1NBQ0Y7UUFFRCxNQUFNLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxHQUFHLGFBQWEsQ0FBQztRQUV6QyxNQUFNLGFBQWEsR0FBRyxRQUFRLENBQUMsV0FBVyxDQUFDLENBQUMsTUFBTSxDQUFDO1FBQ25ELE1BQU0sY0FBYyxHQUFHLFFBQVEsQ0FBQyxRQUFRLENBQUMsYUFBYSxDQUFDLENBQUM7UUFFeEQsTUFBTSxVQUFVLEdBQXNCO1lBQ3BDLElBQUksRUFBRSxRQUFRLENBQUMsSUFBSTtZQUNuQixPQUFPLEVBQUUsYUFBYTtZQUN0QixJQUFJLEVBQUUsUUFBUSxDQUFDLElBQUk7WUFDbkIsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLFVBQVU7WUFDbEQsUUFBUSxFQUFFLFFBQVEsQ0FBQyxRQUFRLElBQUksRUFBRTtZQUNqQyxRQUFRLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDO1lBQ3hDLFdBQVcsRUFBRSxRQUFRLENBQUMsV0FBVztZQUNqQyxPQUFPLEVBQUUsT0FBTyxRQUFRLENBQUMsT0FBTyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxPQUFPO1lBQ3pGLFdBQVcsRUFBRSxRQUFRLENBQUMsV0FBVztZQUNqQyxNQUFNLEVBQUUsSUFBQSwwQkFBWSxFQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUM7WUFDckMsV0FBVyxFQUFFLFFBQVEsQ0FBQyxXQUFXLENBQUM7WUFDbEMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDO1lBQ2xDLE9BQU8sRUFBRSxRQUFRLENBQUMsSUFBSSxDQUFDLE9BQU87WUFDOUIsUUFBUSxFQUFFLFFBQVEsQ0FBQyxJQUFJLENBQUMsUUFBUTtZQUNoQywyQkFBMkI7WUFDM0IscUJBQXFCLEVBQUUsUUFBUSxDQUFDLHFCQUFxQjtZQUNyRCxvQkFBb0I7WUFDcEIsUUFBUSxFQUFFLGNBQWMsRUFBRSxRQUFRO1lBQ2xDLFdBQVc7WUFDWCxZQUFZLEVBQUUsY0FBYyxFQUFFLFlBQVk7U0FDM0MsQ0FBQztRQUVGLHFGQUFxRjtRQUNyRix5QkFBeUI7UUFDekIsSUFBSSxVQUFVLENBQUMsV0FBVyxFQUFFO1lBQzFCLFVBQVUsQ0FBQyxXQUFXLEdBQUcsVUFBVSxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLEVBQUU7Z0JBQy9ELE9BQU87b0JBQ0wsUUFBUSxFQUFFLFVBQVUsQ0FBQyxJQUFJO29CQUN6QixHQUFHLFVBQVU7aUJBQ2QsQ0FBQztZQUNKLENBQUMsQ0FBQyxDQUFDO1NBQ0o7UUFFRCxNQUFNLFFBQVEsR0FBdUI7WUFDbkMsT0FBTyxFQUFFLFVBQVU7WUFDbkIsU0FBUyxFQUFFO2dCQUNULEdBQUcsRUFBRSxZQUFZO2FBQ2xCO1NBQ0YsQ0FBQztRQUVGLE9BQU8sTUFBTSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQzdELENBQUM7SUFFRCxLQUFLLENBQUMsYUFBYSxDQUFDLElBQVksRUFBRSxJQUFZLEVBQUUsSUFBWTtRQUMxRCxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDbkQsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixDQUFDO1lBQ3pDLElBQUk7WUFDSixXQUFXLEVBQUUsSUFBSTtTQUNsQixDQUFDLENBQUM7UUFFSCxNQUFNLEdBQUcsR0FBRyxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxhQUFhLENBQUM7WUFDcEQsSUFBSSxFQUFFO2dCQUNKLElBQUk7Z0JBQ0osSUFBSTtnQkFDSixLQUFLLEVBQUU7b0JBQ0wsY0FBYyxFQUFFO3dCQUNkLFVBQVUsRUFBRSxTQUFTO3dCQUNyQixLQUFLLEVBQUU7NEJBQ0wsSUFBSSxFQUFFO2dDQUNKLE1BQU0sRUFBRSxZQUFZO2dDQUNwQixvQkFBb0IsRUFBRSxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7NkJBQ2xEO3lCQUNGO3dCQUNELFlBQVksRUFBRSxXQUFXO3FCQUMxQjtpQkFDRjthQUNGO1NBQ0YsQ0FBQyxDQUFDO1FBQ0gsTUFBTSxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsR0FBRyxHQUFHLENBQUM7UUFDNUIsT0FBTztZQUNMLE9BQU8sRUFBRSxJQUFJLEVBQUUsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFO2dCQUN4QixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUM7WUFDdEIsQ0FBQyxDQUFDO1lBQ0YsS0FBSyxFQUFHLEtBQWlDLENBQUMsS0FBSztTQUNoRCxDQUFDO0lBQ0osQ0FBQztJQUVELEtBQUssQ0FBQyxhQUFhLENBQUMsUUFBZ0I7UUFDbEMsSUFBSTtZQUNGLE9BQU8sTUFBTSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1NBQzVEO1FBQUMsT0FBTyxLQUFLLEVBQUU7WUFDZCxpREFBaUQ7WUFDakQsSUFBSSxLQUFLLFlBQVksc0JBQU0sQ0FBQyxhQUFhLElBQUksS0FBSyxFQUFFLFVBQVUsS0FBSyxHQUFHLEVBQUU7Z0JBQ3RFLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLGtFQUFrRSxFQUFFLFFBQVEsQ0FBQyxDQUFDO2dCQUMvRixPQUFPLFFBQVEsQ0FBQzthQUNqQjtZQUNELE1BQU0sS0FBSyxDQUFDO1NBQ2I7SUFDSCxDQUFDO0lBRUQsMEVBQTBFO0lBQ2xFLGtCQUFrQixDQUFDLElBQVk7UUFDckMsSUFBSSxDQUFDLElBQUksRUFBRTtZQUNULE9BQU8sRUFBRSxDQUFDO1NBQ1g7UUFDRCxPQUFPO1lBQ0wsb0NBQW9DO1lBQ3BDO2dCQUNFLFdBQVcsRUFBRTtvQkFDWCxLQUFLLEVBQUUsSUFBSTtvQkFDWCxRQUFRLEVBQUUsS0FBSztvQkFDZixNQUFNLEVBQUU7d0JBQ04seUJBQXlCO3dCQUN6Qiw4QkFBOEI7d0JBQzlCLDZCQUE2QjtxQkFDOUI7b0JBQ0QsSUFBSSxFQUFFLGNBQWM7b0JBQ3BCLEtBQUssRUFBRSxDQUFDO29CQUNSLFdBQVcsRUFBRSxHQUFHO2lCQUNqQjthQUNGO1lBRUQsaUNBQWlDO1lBQ2pDO2dCQUNFLFdBQVcsRUFBRTtvQkFDWCxLQUFLLEVBQUUsSUFBSTtvQkFDWCxRQUFRLEVBQUUsS0FBSztvQkFDZixNQUFNLEVBQUU7d0JBQ04sMkJBQTJCO3dCQUMzQixnQ0FBZ0M7d0JBQ2hDLCtCQUErQjtxQkFDaEM7b0JBQ0QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsSUFBSSxFQUFFLENBQUM7b0JBQ1AsS0FBSyxFQUFFLENBQUM7b0JBQ1IsV0FBVyxFQUFFLEdBQUc7aUJBQ2pCO2FBQ0Y7WUFFRCw0Q0FBNEM7WUFDNUM7Z0JBQ0UsV0FBVyxFQUFFO29CQUNYLEtBQUssRUFBRSxJQUFJO29CQUNYLFFBQVEsRUFBRSxLQUFLO29CQUNmLE1BQU0sRUFBRTt3QkFDTiw2QkFBNkI7d0JBQzdCLGtDQUFrQzt3QkFDbEMsaUNBQWlDO3FCQUNsQztvQkFDRCxJQUFJLEVBQUUsY0FBYztvQkFDcEIsS0FBSyxFQUFFLENBQUM7b0JBQ1IsV0FBVyxFQUFFLEdBQUc7aUJBQ2pCO2FBQ0Y7WUFFRCw4RUFBOEU7WUFDOUU7Z0JBQ0UsV0FBVyxFQUFFO29CQUNYLEtBQUssRUFBRSxJQUFJO29CQUNYLFFBQVEsRUFBRSxLQUFLO29CQUNmLE1BQU0sRUFBRTt3QkFDTix3Q0FBd0M7d0JBQ3hDLDZDQUE2Qzt3QkFDN0MsNENBQTRDO3FCQUM3QztvQkFDRCxJQUFJLEVBQUUsY0FBYztvQkFDcEIsV0FBVyxFQUFFLEdBQUc7aUJBQ2pCO2FBQ0Y7U0FDRixDQUFDO0lBQ0osQ0FBQztJQUVPLGlCQUFpQixDQUFDLE1BQXlEO1FBQ2pGLGdEQUFnRDtRQUNoRCxNQUFNLFNBQVMsR0FBRyw0QkFBNEIsQ0FBQztRQUMvQyxNQUFNLE1BQU0sR0FBRyxnRUFBZ0UsU0FBUyx3QkFBd0IsU0FBUyx1QkFBdUIsQ0FBQztRQUNqSixPQUFPO1lBQ0wsTUFBTSxFQUFFO2dCQUNOLE1BQU07Z0JBQ04sTUFBTSxFQUFFO29CQUNOLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxJQUFJLEVBQUU7b0JBQ3ZCLFdBQVcsRUFBRSxNQUFNLENBQUMsV0FBVztpQkFDaEM7YUFDRjtTQUNGLENBQUM7SUFDSixDQUFDO0NBQ0YsQ0FBQTtBQXhPWSxvREFBb0I7QUFFZDtJQURoQixJQUFBLGFBQU0sR0FBRTs4QkFDK0IsNkNBQXFCO21FQUFDO0FBRTdDO0lBRGhCLElBQUEsYUFBTSxHQUFFOzhCQUMwQixtQ0FBZ0I7OERBQUM7QUFFNUM7SUFEUCxJQUFBLGFBQU0sR0FBRTs4QkFDaUMsbUVBQWdDOzhFQUFDO0FBRWpFO0lBRFQsSUFBQSxhQUFNLEdBQUU7OEJBQ29CLHFDQUFpQjsrREFBQztBQUVyQztJQURULElBQUEsYUFBTSxHQUFFOzhCQUNnQyw2REFBNkI7MkVBQUM7K0JBVjVELG9CQUFvQjtJQUhoQyxJQUFBLHFCQUFjLEVBQUM7UUFDZCxXQUFXLEVBQUUsa0JBQVcsQ0FBQyxNQUFNO0tBQ2hDLENBQUM7R0FDVyxvQkFBb0IsQ0F3T2hDIn0=