cnpmcore
Version:
910 lines • 88.6 kB
JavaScript
"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 };
};
var PackageManagerService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PackageManagerService = void 0;
const promises_1 = require("node:fs/promises");
const tegg_1 = require("@eggjs/tegg");
const egg_errors_1 = require("egg-errors");
const npm_package_arg_1 = __importDefault(require("npm-package-arg"));
const semver_1 = __importDefault(require("semver"));
const PackageUtil_1 = require("../../common/PackageUtil");
const AbstractService_1 = require("../../common/AbstractService");
const PackageRepository_1 = require("../../repository/PackageRepository");
const PackageVersionBlockRepository_1 = require("../../repository/PackageVersionBlockRepository");
const PackageVersionDownloadRepository_1 = require("../../repository/PackageVersionDownloadRepository");
const DistRepository_1 = require("../../repository/DistRepository");
const Package_1 = require("../entity/Package");
const PackageVersion_1 = require("../entity/PackageVersion");
const PackageVersionBlock_1 = require("../entity/PackageVersionBlock");
const PackageTag_1 = require("../entity/PackageTag");
const event_1 = require("../event");
const BugVersionService_1 = require("./BugVersionService");
const RegistryManagerService_1 = require("./RegistryManagerService");
const PackageVersionService_1 = require("./PackageVersionService");
const TOTAL = '@@TOTAL@@';
const SCOPE_TOTAL_PREFIX = '@@SCOPE@@:';
const DESCRIPTION_LIMIT = 1024 * 10;
let PackageManagerService = PackageManagerService_1 = class PackageManagerService extends AbstractService_1.AbstractService {
// support user publish private package and sync worker publish public package
async publish(cmd, publisher) {
let pkg = await this.packageRepository.findPackage(cmd.scope, cmd.name);
if (!pkg) {
pkg = Package_1.Package.create({
scope: cmd.scope,
name: cmd.name,
isPrivate: cmd.isPrivate,
description: cmd.description || '',
registryId: cmd.registryId,
});
}
else {
// update description
// will read database twice to update description by model to entity and entity to model
if (pkg.description !== cmd.description) {
pkg.description = cmd.description || '';
}
/* c8 ignore next 3 */
// package can be migrated into another registry
if (cmd.registryId) {
pkg.registryId = cmd.registryId;
}
}
// 防止 description 长度超过 db 限制
if (pkg.description?.length > DESCRIPTION_LIMIT) {
pkg.description = pkg.description.substring(0, DESCRIPTION_LIMIT);
}
await this.packageRepository.savePackage(pkg);
// create maintainer
await this.packageRepository.savePackageMaintainer(pkg.packageId, publisher.userId);
let pkgVersion = await this.packageRepository.findPackageVersion(pkg.packageId, cmd.version);
if (pkgVersion) {
throw new egg_errors_1.ForbiddenError(`Can't modify pre-existing version: ${pkg.fullname}@${pkgVersion.version}`);
}
// make sure cmd.packageJson.readme is deleted
if ('readme' in cmd.packageJson) {
delete cmd.packageJson.readme;
}
const publishTime = cmd.publishTime || new Date();
// add _cnpmcore_publish_time field to cmd.packageJson
if (!cmd.packageJson._cnpmcore_publish_time) {
cmd.packageJson._cnpmcore_publish_time = publishTime;
}
if (!cmd.packageJson.publish_time) {
cmd.packageJson.publish_time = publishTime.getTime();
}
if (cmd.packageJson._hasShrinkwrap === undefined) {
cmd.packageJson._hasShrinkwrap = await (0, PackageUtil_1.hasShrinkWrapInTgz)(cmd.dist.content || cmd.dist.localFile);
}
// set _npmUser field to cmd.packageJson
cmd.packageJson._npmUser = {
// clean user scope prefix
name: publisher.displayName,
email: publisher.email,
};
// add _registry_name field to cmd.packageJson
const registry = await this.getSourceRegistry(pkg);
if (registry) {
cmd.packageJson._source_registry_name = registry.name;
}
// https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-version-object
const hasInstallScript = (0, PackageUtil_1.detectInstallScript)(cmd.packageJson) ? true : undefined;
let tarDistIntegrity;
let tarDistSize = 0;
if (cmd.dist.content) {
const tarDistBytes = cmd.dist.content;
tarDistIntegrity = await (0, PackageUtil_1.calculateIntegrity)(tarDistBytes);
tarDistSize = tarDistBytes.length;
}
else if (cmd.dist.localFile) {
const localFile = cmd.dist.localFile;
const fileStat = await (0, promises_1.stat)(localFile);
tarDistIntegrity = await (0, PackageUtil_1.calculateIntegrity)(localFile);
tarDistSize = fileStat.size;
}
const tarDist = pkg.createTar(cmd.version, {
size: tarDistSize,
shasum: tarDistIntegrity.shasum,
integrity: tarDistIntegrity.integrity,
});
if (cmd.dist.content) {
await this.distRepository.saveDist(tarDist, cmd.dist.content);
}
else if (cmd.dist.localFile) {
await this.distRepository.saveDist(tarDist, cmd.dist.localFile);
}
cmd.packageJson.dist = {
...cmd.packageJson.dist,
tarball: (0, PackageUtil_1.formatTarball)(this.config.cnpmcore.registry, pkg.scope, pkg.name, cmd.version),
size: tarDistSize,
shasum: tarDistIntegrity.shasum,
integrity: tarDistIntegrity.integrity,
};
// https://github.com/npm/registry/blob/main/docs/responses/package-metadata.md#abbreviated-version-object
// Abbreviated version object
const abbreviated = JSON.stringify({
name: cmd.packageJson.name,
version: cmd.packageJson.version,
deprecated: cmd.packageJson.deprecated,
dependencies: cmd.packageJson.dependencies,
acceptDependencies: cmd.packageJson.acceptDependencies,
optionalDependencies: cmd.packageJson.optionalDependencies,
devDependencies: cmd.packageJson.devDependencies,
bundleDependencies: cmd.packageJson.bundleDependencies,
peerDependencies: cmd.packageJson.peerDependencies,
peerDependenciesMeta: cmd.packageJson.peerDependenciesMeta,
bin: cmd.packageJson.bin,
directories: cmd.packageJson.directories,
os: cmd.packageJson.os,
cpu: cmd.packageJson.cpu,
libc: cmd.packageJson.libc,
workspaces: cmd.packageJson.workspaces,
dist: cmd.packageJson.dist,
engines: cmd.packageJson.engines,
_hasShrinkwrap: cmd.packageJson._hasShrinkwrap,
hasInstallScript,
funding: cmd.packageJson.funding,
// https://github.com/cnpm/npminstall/blob/13efc7eec21a61e509226e3772bfb75cd5605612/lib/install_package.js#L176
// npminstall require publish time to show the recently update versions
publish_time: cmd.packageJson.publish_time,
_source_registry_name: cmd.packageJson._source_registry_name,
});
const abbreviatedDistBytes = Buffer.from(abbreviated);
const abbreviatedDistIntegrity = await (0, PackageUtil_1.calculateIntegrity)(abbreviatedDistBytes);
const readmeDistBytes = Buffer.from(cmd.readme);
const readmeDistIntegrity = await (0, PackageUtil_1.calculateIntegrity)(readmeDistBytes);
const manifestDistBytes = Buffer.from(JSON.stringify(cmd.packageJson));
const manifestDistIntegrity = await (0, PackageUtil_1.calculateIntegrity)(manifestDistBytes);
pkgVersion = PackageVersion_1.PackageVersion.create({
packageId: pkg.packageId,
version: cmd.version,
publishTime,
manifestDist: pkg.createManifest(cmd.version, {
size: manifestDistBytes.length,
shasum: manifestDistIntegrity.shasum,
integrity: manifestDistIntegrity.integrity,
}),
readmeDist: pkg.createReadme(cmd.version, {
size: readmeDistBytes.length,
shasum: readmeDistIntegrity.shasum,
integrity: readmeDistIntegrity.integrity,
}),
abbreviatedDist: pkg.createAbbreviated(cmd.version, {
size: abbreviatedDistBytes.length,
shasum: abbreviatedDistIntegrity.shasum,
integrity: abbreviatedDistIntegrity.integrity,
}),
tarDist,
});
await Promise.all([
this.distRepository.saveDist(pkgVersion.abbreviatedDist, abbreviatedDistBytes),
this.distRepository.saveDist(pkgVersion.manifestDist, manifestDistBytes),
this.distRepository.saveDist(pkgVersion.readmeDist, readmeDistBytes),
]);
try {
await this.packageRepository.createPackageVersion(pkgVersion);
}
catch (e) {
if (e.code === 'ER_DUP_ENTRY') {
throw new egg_errors_1.ForbiddenError(`Can't modify pre-existing version: ${pkg.fullname}@${cmd.version}`);
}
throw e;
}
if (cmd.skipRefreshPackageManifests !== true) {
await this.refreshPackageChangeVersionsToDists(pkg, [pkgVersion.version]);
}
if (cmd.tags) {
for (const tag of cmd.tags) {
await this.savePackageTag(pkg, tag, cmd.version, true);
this.eventBus.emit(event_1.PACKAGE_VERSION_ADDED, pkg.fullname, pkgVersion.version, tag);
}
}
else {
this.eventBus.emit(event_1.PACKAGE_VERSION_ADDED, pkg.fullname, pkgVersion.version, undefined);
}
return pkgVersion;
}
async blockPackageByFullname(name, reason) {
const [scope, pkgName] = (0, PackageUtil_1.getScopeAndName)(name);
const pkg = await this.packageRepository.findPackage(scope, pkgName);
if (!pkg) {
throw new egg_errors_1.NotFoundError(`Package name(${name}) not found`);
}
return await this.blockPackage(pkg, reason);
}
async blockPackage(pkg, reason) {
let block = await this.packageVersionBlockRepository.findPackageBlock(pkg.packageId);
if (block) {
block.reason = reason;
}
else {
block = PackageVersionBlock_1.PackageVersionBlock.create({
packageId: pkg.packageId,
version: '*',
reason,
});
}
await this.packageVersionBlockRepository.savePackageVersionBlock(block);
if (pkg.manifestsDist && pkg.abbreviatedsDist) {
const fullManifests = await this.distRepository.readDistBytesToJSON(pkg.manifestsDist);
if (fullManifests) {
fullManifests.block = reason;
}
const abbreviatedManifests = await this.distRepository.readDistBytesToJSON(pkg.abbreviatedsDist);
if (abbreviatedManifests) {
abbreviatedManifests.block = reason;
}
await this._updatePackageManifestsToDists(pkg, fullManifests || null, abbreviatedManifests || null);
this.eventBus.emit(event_1.PACKAGE_BLOCKED, pkg.fullname);
this.logger.info('[packageManagerService.blockPackage:success] packageId: %s, reason: %j', pkg.packageId, reason);
}
return block;
}
async unblockPackageByFullname(name) {
const [scope, pkgName] = (0, PackageUtil_1.getScopeAndName)(name);
const pkg = await this.packageRepository.findPackage(scope, pkgName);
if (!pkg) {
throw new egg_errors_1.NotFoundError(`Package name(${name}) not found`);
}
return await this.unblockPackage(pkg);
}
async unblockPackage(pkg) {
const block = await this.packageVersionBlockRepository.findPackageVersionBlock(pkg.packageId, '*');
if (block) {
await this.packageVersionBlockRepository.removePackageVersionBlock(block.packageVersionBlockId);
}
if (pkg.manifestsDist && pkg.abbreviatedsDist) {
const fullManifests = await this.distRepository.readDistBytesToJSON(pkg.manifestsDist);
if (fullManifests) {
fullManifests.block = undefined;
}
const abbreviatedManifests = await this.distRepository.readDistBytesToJSON(pkg.abbreviatedsDist);
if (abbreviatedManifests) {
abbreviatedManifests.block = undefined;
}
await this._updatePackageManifestsToDists(pkg, fullManifests || null, abbreviatedManifests || null);
this.eventBus.emit(event_1.PACKAGE_UNBLOCKED, pkg.fullname);
this.logger.info('[packageManagerService.unblockPackage:success] packageId: %s', pkg.packageId);
}
}
async replacePackageMaintainersAndDist(pkg, maintainers) {
await this.packageRepository.replacePackageMaintainers(pkg.packageId, maintainers.map(m => m.userId));
await this.refreshPackageMaintainersToDists(pkg);
this.eventBus.emit(event_1.PACKAGE_MAINTAINER_CHANGED, pkg.fullname, maintainers);
}
async savePackageMaintainers(pkg, maintainers) {
let hasNewRecord = false;
for (const maintainer of maintainers) {
const newRecord = await this.packageRepository.savePackageMaintainer(pkg.packageId, maintainer.userId);
if (newRecord) {
hasNewRecord = true;
}
}
if (hasNewRecord) {
this.eventBus.emit(event_1.PACKAGE_MAINTAINER_CHANGED, pkg.fullname, maintainers);
}
}
async removePackageMaintainer(pkg, maintainer) {
await this.packageRepository.removePackageMaintainer(pkg.packageId, maintainer.userId);
this.eventBus.emit(event_1.PACKAGE_MAINTAINER_REMOVED, pkg.fullname, maintainer.name);
}
async refreshPackageMaintainersToDists(pkg) {
await this._refreshPackageManifestRootAttributeOnlyToDists(pkg, 'maintainers');
}
async refreshPackageDistTagsToDists(pkg) {
await this._refreshPackageManifestRootAttributeOnlyToDists(pkg, 'dist-tags');
}
async listPackageFullManifests(scope, name, isSync = false) {
return await this._listPackageFullOrAbbreviatedManifests(scope, name, true, isSync);
}
async listPackageAbbreviatedManifests(scope, name, isSync = false) {
return await this._listPackageFullOrAbbreviatedManifests(scope, name, false, isSync);
}
async showPackageVersionByVersionOrTag(scope, name, spec) {
const pkg = await this.packageRepository.findPackage(scope, name);
if (!pkg)
return {};
const block = await this.packageVersionBlockRepository.findPackageBlock(pkg.packageId);
if (block) {
return { blockReason: block.reason, pkg };
}
const fullname = (0, PackageUtil_1.getFullname)(scope, name);
const result = (0, npm_package_arg_1.default)(`${fullname}@${spec}`);
const version = await this.packageVersionService.getVersion(result);
if (!version) {
return {};
}
const packageVersion = await this.packageRepository.findPackageVersion(pkg.packageId, version);
return { packageVersion, pkg };
}
async showPackageVersionManifest(scope, name, spec, isSync = false, isFullManifests = false) {
const pkg = await this.packageRepository.findPackage(scope, name);
if (!pkg)
return {};
const block = await this.packageVersionBlockRepository.findPackageBlock(pkg.packageId);
if (block) {
return { blockReason: block.reason, pkg };
}
const fullname = (0, PackageUtil_1.getFullname)(scope, name);
const result = (0, npm_package_arg_1.default)(`${fullname}@${spec}`);
const manifest = await this.packageVersionService.readManifest(pkg.packageId, result, isFullManifests, !isSync);
return { manifest, blockReason: null, pkg };
}
async downloadPackageVersionTar(packageVersion) {
return await this.distRepository.downloadDist(packageVersion.tarDist);
}
plusPackageVersionCounter(fullname, version) {
// set counter + 1, schedule will store them into database
const counters = PackageManagerService_1.downloadCounters;
if (!counters[fullname])
counters[fullname] = {};
counters[fullname][version] = (counters[fullname][version] || 0) + 1;
// Total
const ALL = '*';
if (!counters[TOTAL])
counters[TOTAL] = {};
counters[TOTAL][ALL] = (counters[TOTAL][ALL] || 0) + 1;
// scope total
const scope = (0, PackageUtil_1.getScopeAndName)(fullname)[0];
if (scope) {
const scopeKey = `${SCOPE_TOTAL_PREFIX}${scope}`;
if (!counters[scopeKey])
counters[scopeKey] = {};
counters[scopeKey][ALL] = (counters[scopeKey][ALL] || 0) + 1;
}
}
// will be call by schedule/SavePackageVersionDownloadCounter.ts
async savePackageVersionCounters() {
// { [fullname]: { [version]: number } }
const counters = PackageManagerService_1.downloadCounters;
const fullnames = Object.keys(counters);
if (fullnames.length === 0)
return;
PackageManagerService_1.downloadCounters = {};
this.logger.info('[packageManagerService.savePackageVersionCounters:saving] %d fullnames', fullnames.length);
let total = 0;
for (const fullname in counters) {
const versions = counters[fullname];
let packageId = null;
if (fullname === TOTAL) {
packageId = 'total';
}
else if (fullname.startsWith(SCOPE_TOTAL_PREFIX)) {
packageId = fullname.replace(SCOPE_TOTAL_PREFIX, '');
}
else {
// find packageId from fullname
const [scope, name] = (0, PackageUtil_1.getScopeAndName)(fullname);
packageId = await this.packageRepository.findPackageId(scope, name);
}
if (!packageId)
continue;
for (const version in versions) {
const counter = versions[version];
await this.packageVersionDownloadRepository.plus(packageId, version, counter);
total += counter;
}
}
this.logger.info('[packageManagerService.savePackageVersionCounters:saved] %d total', total);
}
async saveDeprecatedVersions(pkg, deprecatedList) {
const updateVersions = [];
for (const { version, deprecated } of deprecatedList) {
const pkgVersion = await this.packageRepository.findPackageVersion(pkg.packageId, version);
if (!pkgVersion)
continue;
const message = deprecated === '' ? undefined : deprecated;
await this._mergeManifestDist(pkgVersion.manifestDist, { deprecated: message });
await this._mergeManifestDist(pkgVersion.abbreviatedDist, { deprecated: message });
await this.packageRepository.savePackageVersion(pkgVersion);
updateVersions.push(version);
}
await this.refreshPackageChangeVersionsToDists(pkg, updateVersions);
this.eventBus.emit(event_1.PACKAGE_META_CHANGED, pkg.fullname, { deprecateds: deprecatedList });
}
async savePackageVersionManifest(pkgVersion, mergeManifest, mergeAbbreviated) {
await this._mergeManifestDist(pkgVersion.manifestDist, mergeManifest);
await this._mergeManifestDist(pkgVersion.abbreviatedDist, mergeAbbreviated);
}
/**
* save package version readme
*/
async savePackageVersionReadme(pkgVersion, readmeFile) {
await this.distRepository.saveDist(pkgVersion.readmeDist, readmeFile);
this.logger.info('[PackageManagerService.savePackageVersionReadme] save packageVersionId:%s readme:%s to dist:%s', pkgVersion.packageVersionId, readmeFile, pkgVersion.readmeDist.distId);
}
async savePackageReadme(pkg, readmeFile) {
if (!pkg.manifestsDist)
return;
const fullManifests = await this.distRepository.readDistBytesToJSON(pkg.manifestsDist);
if (!fullManifests)
return;
fullManifests.readme = await (0, promises_1.readFile)(readmeFile, 'utf-8');
await this._updatePackageManifestsToDists(pkg, fullManifests, null);
this.logger.info('[PackageManagerService.savePackageReadme] save packageId:%s readme, size: %s', pkg.packageId, fullManifests.readme.length);
}
async _removePackageVersionAndDist(pkgVersion) {
// remove nfs dists
await Promise.all([
this.distRepository.destroyDist(pkgVersion.abbreviatedDist),
this.distRepository.destroyDist(pkgVersion.manifestDist),
this.distRepository.destroyDist(pkgVersion.readmeDist),
this.distRepository.destroyDist(pkgVersion.tarDist),
]);
// remove from repository
await this.packageRepository.removePackageVersion(pkgVersion);
}
async unpublishPackage(pkg) {
const pkgVersions = await this.packageRepository.listPackageVersions(pkg.packageId);
// already unpublished
if (pkgVersions.length === 0) {
this.logger.info(`[packageManagerService.unpublishPackage:skip] ${pkg.packageId} already unpublished`);
return;
}
for (const pkgVersion of pkgVersions) {
await this._removePackageVersionAndDist(pkgVersion);
}
// set unpublished dist to package's manifestDist and abbreviatedDist
const unpublishedInfo = {
_id: pkg.fullname,
name: pkg.fullname,
time: {
created: pkg.createdAt,
modified: pkg.updatedAt,
unpublished: new Date(),
},
// keep this property exists for forward compatibility
// https://github.com/cnpm/cnpmjs.org/blob/ad622d55e384743b48e79bb6aec574a7f354ee9f/controllers/sync_module_worker.js#L828
'dist-tags': {},
};
await this._mergeManifestDist(pkg.manifestsDist, undefined, unpublishedInfo);
await this._mergeManifestDist(pkg.abbreviatedsDist, undefined, unpublishedInfo);
this.eventBus.emit(event_1.PACKAGE_UNPUBLISHED, pkg.fullname);
}
async removePackageVersion(pkg, pkgVersion, skipRefreshPackageManifests = false) {
const currentVersions = await this.packageRepository.listPackageVersionNames(pkg.packageId);
// only one version, unpublish the package
if (currentVersions.length === 1 && currentVersions[0] === pkgVersion.version) {
await this.unpublishPackage(pkg);
return;
}
// remove version & update tags
await this._removePackageVersionAndDist(pkgVersion);
const versions = await this.packageRepository.listPackageVersionNames(pkg.packageId);
if (versions.length > 0) {
let updateTag;
// make sure latest tag exists
const latestTag = await this.packageRepository.findPackageTag(pkg.packageId, 'latest');
if (latestTag?.version === pkgVersion.version) {
// change latest version
// https://github.com/npm/libnpmpublish/blob/main/unpublish.js#L62
const latestVersion = versions.sort(semver_1.default.compareLoose).pop();
if (latestVersion) {
updateTag = latestTag.tag;
await this.savePackageTag(pkg, latestTag.tag, latestVersion, true);
}
}
if (skipRefreshPackageManifests !== true) {
await this.refreshPackageChangeVersionsToDists(pkg, undefined, [pkgVersion.version]);
this.eventBus.emit(event_1.PACKAGE_VERSION_REMOVED, pkg.fullname, pkgVersion.version, updateTag);
}
return;
}
}
async savePackageTag(pkg, tag, version, skipEvent = false) {
let tagEntity = await this.packageRepository.findPackageTag(pkg.packageId, tag);
if (!tagEntity) {
tagEntity = PackageTag_1.PackageTag.create({
packageId: pkg.packageId,
tag,
version,
});
await this.packageRepository.savePackageTag(tagEntity);
await this._refreshPackageManifestRootAttributeOnlyToDists(pkg, 'dist-tags');
if (!skipEvent) {
this.eventBus.emit(event_1.PACKAGE_TAG_ADDED, pkg.fullname, tagEntity.tag);
}
return true;
}
if (tagEntity.version === version) {
// nothing change
return false;
}
tagEntity.version = version;
await this.packageRepository.savePackageTag(tagEntity);
await this._refreshPackageManifestRootAttributeOnlyToDists(pkg, 'dist-tags');
if (!skipEvent) {
this.eventBus.emit(event_1.PACKAGE_TAG_CHANGED, pkg.fullname, tagEntity.tag);
}
return true;
}
async removePackageTag(pkg, tag) {
const tagEntity = await this.packageRepository.findPackageTag(pkg.packageId, tag);
if (!tagEntity)
return false;
await this.packageRepository.removePackageTag(tagEntity);
await this._refreshPackageManifestRootAttributeOnlyToDists(pkg, 'dist-tags');
this.eventBus.emit(event_1.PACKAGE_TAG_REMOVED, pkg.fullname, tagEntity.tag);
return true;
}
async refreshPackageChangeVersionsToDists(pkg, updateVersions, removeVersions) {
if (!pkg.manifestsDist?.distId || !pkg.abbreviatedsDist?.distId) {
return await this._refreshPackageManifestsToDists(pkg);
}
const fullManifests = await this.distRepository.readDistBytesToJSON(pkg.manifestsDist);
const abbreviatedManifests = await this.distRepository.readDistBytesToJSON(pkg.abbreviatedsDist);
if (!fullManifests?.versions || !abbreviatedManifests?.versions) {
// is unpublished, refresh all again
return await this._refreshPackageManifestsToDists(pkg);
}
if (updateVersions) {
for (const version of updateVersions) {
const packageVersion = await this.packageRepository.findPackageVersion(pkg.packageId, version);
if (packageVersion) {
const manifest = await this.distRepository.readDistBytesToJSON(packageVersion.manifestDist);
if (!manifest)
continue;
if ('readme' in manifest) {
delete manifest.readme;
}
fullManifests.versions[packageVersion.version] = manifest;
fullManifests.time[packageVersion.version] = packageVersion.publishTime;
const abbreviatedManifest = await this.distRepository.readDistBytesToJSON(packageVersion.abbreviatedDist);
if (abbreviatedManifest) {
abbreviatedManifests.versions[packageVersion.version] = abbreviatedManifest;
}
}
}
}
if (removeVersions) {
for (const version of removeVersions) {
delete fullManifests.versions[version];
delete fullManifests.time[version];
delete abbreviatedManifests.versions[version];
}
}
// update dist-tags
await this._setPackageDistTagsAndLatestInfos(pkg, fullManifests, abbreviatedManifests);
// store to nfs dist
await this._updatePackageManifestsToDists(pkg, fullManifests, abbreviatedManifests);
}
async getSourceRegistry(pkg) {
let registry;
if (pkg.registryId) {
registry = await this.registryManagerService.findByRegistryId(pkg.registryId);
}
else {
registry = await this.registryManagerService.ensureDefaultRegistry();
}
return registry;
}
async _listPackageDistTags(pkg) {
const tags = await this.packageRepository.listPackageTags(pkg.packageId);
const distTags = {};
for (const tag of tags) {
distTags[tag.tag] = tag.version;
}
return distTags;
}
// refresh package full manifests and abbreviated manifests to NFS
async _refreshPackageManifestsToDists(pkg) {
const [fullManifests, abbreviatedManifests,] = await Promise.all([
await this._listPackageFullManifests(pkg),
await this._listPackageAbbreviatedManifests(pkg),
]);
await this._updatePackageManifestsToDists(pkg, fullManifests, abbreviatedManifests);
}
// only refresh root attributes only, e.g.: dist-tags, maintainers
async _refreshPackageManifestRootAttributeOnlyToDists(pkg, refreshAttr) {
if (refreshAttr === 'maintainers') {
const fullManifests = await this.distRepository.readDistBytesToJSON(pkg.manifestsDist);
const maintainers = await this._listPackageMaintainers(pkg);
if (fullManifests) {
fullManifests.maintainers = maintainers;
await this._updatePackageManifestsToDists(pkg, fullManifests, null);
}
}
else if (refreshAttr === 'dist-tags') {
const fullManifests = await this.distRepository.readDistBytesToJSON(pkg.manifestsDist);
if (fullManifests) {
const abbreviatedManifests = await this.distRepository.readDistBytesToJSON(pkg.abbreviatedsDist);
if (abbreviatedManifests) {
await this._setPackageDistTagsAndLatestInfos(pkg, fullManifests, abbreviatedManifests);
}
await this._updatePackageManifestsToDists(pkg, fullManifests, abbreviatedManifests || null);
}
}
}
_mergeLatestManifestFields(fullManifests, latestManifest) {
if (!latestManifest)
return;
// https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#full-metadata-format
const fieldsFromLatestManifest = [
'author', 'bugs', 'contributors', 'description', 'homepage', 'keywords', 'license',
'readmeFilename', 'repository',
];
// the latest version metas
for (const field of fieldsFromLatestManifest) {
if (latestManifest[field]) {
fullManifests[field] = latestManifest[field];
}
}
}
async _setPackageDistTagsAndLatestInfos(pkg, fullManifests, abbreviatedManifests) {
const distTags = await this._listPackageDistTags(pkg);
if (distTags.latest) {
const packageVersion = await this.packageRepository.findPackageVersion(pkg.packageId, distTags.latest);
if (packageVersion) {
fullManifests.readme = await this.distRepository.readDistBytesToString(packageVersion.readmeDist);
const latestManifest = await this.distRepository.readDistBytesToJSON(packageVersion.manifestDist);
this._mergeLatestManifestFields(fullManifests, latestManifest || null);
}
}
fullManifests['dist-tags'] = distTags;
abbreviatedManifests['dist-tags'] = distTags;
}
async _mergeManifestDist(manifestDist, mergeData, replaceData) {
let manifest = await this.distRepository.readDistBytesToJSON(manifestDist);
if (mergeData && manifest) {
Object.assign(manifest, mergeData);
}
if (replaceData) {
manifest = replaceData;
}
const manifestBytes = Buffer.from(JSON.stringify(manifest));
const manifestIntegrity = await (0, PackageUtil_1.calculateIntegrity)(manifestBytes);
manifestDist.size = manifestBytes.length;
manifestDist.shasum = manifestIntegrity.shasum;
manifestDist.integrity = manifestIntegrity.integrity;
await this.distRepository.saveDist(manifestDist, manifestBytes);
}
async _updatePackageManifestsToDists(pkg, fullManifests, abbreviatedManifests) {
const modified = new Date();
if (fullManifests) {
fullManifests.time.modified = modified;
// same to dist
const fullManifestsDistBytes = Buffer.from(JSON.stringify(fullManifests));
const fullManifestsDistIntegrity = await (0, PackageUtil_1.calculateIntegrity)(fullManifestsDistBytes);
if (pkg.manifestsDist?.distId) {
pkg.manifestsDist.size = fullManifestsDistBytes.length;
pkg.manifestsDist.shasum = fullManifestsDistIntegrity.shasum;
pkg.manifestsDist.integrity = fullManifestsDistIntegrity.integrity;
}
else {
pkg.manifestsDist = pkg.createFullManifests({
size: fullManifestsDistBytes.length,
shasum: fullManifestsDistIntegrity.shasum,
integrity: fullManifestsDistIntegrity.integrity,
});
}
await this.distRepository.saveDist(pkg.manifestsDist, fullManifestsDistBytes);
await this.packageRepository.savePackageDist(pkg, true);
}
if (abbreviatedManifests) {
abbreviatedManifests.modified = modified;
const abbreviatedManifestsDistBytes = Buffer.from(JSON.stringify(abbreviatedManifests));
const abbreviatedManifestsDistIntegrity = await (0, PackageUtil_1.calculateIntegrity)(abbreviatedManifestsDistBytes);
if (pkg.abbreviatedsDist?.distId) {
pkg.abbreviatedsDist.size = abbreviatedManifestsDistBytes.length;
pkg.abbreviatedsDist.shasum = abbreviatedManifestsDistIntegrity.shasum;
pkg.abbreviatedsDist.integrity = abbreviatedManifestsDistIntegrity.integrity;
}
else {
pkg.abbreviatedsDist = pkg.createAbbreviatedManifests({
size: abbreviatedManifestsDistBytes.length,
shasum: abbreviatedManifestsDistIntegrity.shasum,
integrity: abbreviatedManifestsDistIntegrity.integrity,
});
}
await this.distRepository.saveDist(pkg.abbreviatedsDist, abbreviatedManifestsDistBytes);
await this.packageRepository.savePackageDist(pkg, false);
}
}
async _listPackageFullOrAbbreviatedManifests(scope, name, isFullManifests, isSync) {
let etag = '';
let blockReason = '';
const pkg = await this.packageRepository.findPackage(scope, name);
if (!pkg)
return { etag, data: null, blockReason };
const registry = await this.getSourceRegistry(pkg);
const block = await this.packageVersionBlockRepository.findPackageBlock(pkg.packageId);
if (block) {
blockReason = block.reason;
}
let bugVersion;
// sync mode response no bug version fixed
if (!isSync) {
bugVersion = await this.bugVersionService.getBugVersion();
}
const fullname = (0, PackageUtil_1.getFullname)(scope, name);
let dist = isFullManifests ? pkg.manifestsDist : pkg.abbreviatedsDist;
// read from dist
if (dist?.distId) {
etag = `"${dist.shasum}"`;
const data = (await this.distRepository.readDistBytesToJSON(dist));
if (bugVersion) {
await this.bugVersionService.fixPackageBugVersions(bugVersion, fullname, data.versions);
}
// set _source_registry_name in full manifestDist
if (registry) {
data._source_registry_name = registry?.name;
}
const distBytes = Buffer.from(JSON.stringify(data));
const distIntegrity = await (0, PackageUtil_1.calculateIntegrity)(distBytes);
etag = `"${distIntegrity.shasum}"`;
return { etag, data, blockReason };
}
// read from database
const fullManifests = isFullManifests ? await this._listPackageFullManifests(pkg) : null;
const abbreviatedManifests = isFullManifests ? null : await this._listPackageAbbreviatedManifests(pkg);
if (!fullManifests && !abbreviatedManifests) {
// not exists
return { etag, data: null, blockReason };
}
await this._updatePackageManifestsToDists(pkg, fullManifests, abbreviatedManifests);
const manifests = (fullManifests || abbreviatedManifests);
/* c8 ignore next 5 */
if (bugVersion) {
await this.bugVersionService.fixPackageBugVersions(bugVersion, fullname, manifests.versions);
const distBytes = Buffer.from(JSON.stringify(manifests));
const distIntegrity = await (0, PackageUtil_1.calculateIntegrity)(distBytes);
etag = `"${distIntegrity.shasum}"`;
}
else {
dist = isFullManifests ? pkg.manifestsDist : pkg.abbreviatedsDist;
etag = `"${dist.shasum}"`;
}
return { etag, data: manifests, blockReason };
}
async _listPackageMaintainers(pkg) {
const users = await this.packageRepository.listPackageMaintainers(pkg.packageId);
return users.map(({ displayName, email }) => ({ name: displayName, email }));
}
async _listPackageFullManifests(pkg) {
// read all verions from db
const packageVersions = await this.packageRepository.listPackageVersions(pkg.packageId);
if (packageVersions.length === 0)
return null;
const distTags = await this._listPackageDistTags(pkg);
const maintainers = await this._listPackageMaintainers(pkg);
const registry = await this.getSourceRegistry(pkg);
// https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#full-metadata-format
const data = {
_id: `${pkg.fullname}`,
_rev: `${pkg.id}-${pkg.packageId}`,
'dist-tags': distTags,
// the package name
name: pkg.fullname,
// an object mapping versions to the time published, along with created and modified timestamps
time: {
// '1.0.0': '2012-09-18T14:46:08.346Z',
created: pkg.createdAt,
modified: pkg.updatedAt,
},
// a mapping of semver-compliant version numbers to version data
versions: {},
// The following fields are hoisted to the top-level of the package json from the latest version published:
// human object
author: undefined,
bugs: undefined,
description: pkg.description,
homepage: undefined,
keywords: undefined,
// the SPDX identifier of the package's license
license: undefined,
// array of human objects for people with permission to publish this package; not authoritative but informational
maintainers,
// contributors: array of human objects
// the first 64K of the README data for the most-recently published version of the package
readme: '',
// The name of the file from which the readme data was taken
readmeFilename: undefined,
// as given in package.json, for the latest version
repository: undefined,
// users: an object whose keys are the npm user names of people who have starred this package
_source_registry_name: registry?.name,
};
let latestTagVersion = '';
if (distTags.latest) {
latestTagVersion = distTags.latest;
}
let latestManifest;
let latestPackageVersion = packageVersions[0];
// https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#package-metadata
for (const packageVersion of packageVersions) {
const manifest = await this.distRepository.readDistBytesToJSON(packageVersion.manifestDist);
if (!manifest)
continue;
/* c8 ignore next 3 */
if ('readme' in manifest) {
delete manifest.readme;
}
if (latestTagVersion && packageVersion.version === latestTagVersion) {
latestManifest = manifest;
latestPackageVersion = packageVersion;
}
data.versions[packageVersion.version] = manifest;
data.time[packageVersion.version] = packageVersion.publishTime;
}
// the latest version readme
data.readme = await this.distRepository.readDistBytesToString(latestPackageVersion.readmeDist);
if (!latestManifest) {
latestManifest = data.versions[latestPackageVersion.version];
}
this._mergeLatestManifestFields(data, latestManifest);
return data;
}
async _listPackageAbbreviatedManifests(pkg) {
// read all verions from db
const packageVersions = await this.packageRepository.listPackageVersions(pkg.packageId);
if (packageVersions.length === 0)
return null;
const distTags = await this._listPackageDistTags(pkg);
// https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#package-metadata
// tiny-tarball is a small package with only one version and no dependencies.
const data = {
'dist-tags': distTags,
modified: pkg.updatedAt,
name: pkg.fullname,
versions: {},
};
for (const packageVersion of packageVersions) {
const manifest = await this.distRepository.readDistBytesToJSON(packageVersion.abbreviatedDist);
if (manifest) {
data.versions[packageVersion.version] = manifest;
}
}
return data;
}
};
exports.PackageManagerService = PackageManagerService;
PackageManagerService.downloadCounters = {};
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", Object)
], PackageManagerService.prototype, "eventBus", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", PackageRepository_1.PackageRepository)
], PackageManagerService.prototype, "packageRepository", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", PackageVersionBlockRepository_1.PackageVersionBlockRepository)
], PackageManagerService.prototype, "packageVersionBlockRepository", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", PackageVersionDownloadRepository_1.PackageVersionDownloadRepository)
], PackageManagerService.prototype, "packageVersionDownloadRepository", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", BugVersionService_1.BugVersionService)
], PackageManagerService.prototype, "bugVersionService", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", DistRepository_1.DistRepository)
], PackageManagerService.prototype, "distRepository", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", RegistryManagerService_1.RegistryManagerService)
], PackageManagerService.prototype, "registryManagerService", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", PackageVersionService_1.PackageVersionService)
], PackageManagerService.prototype, "packageVersionService", void 0);
exports.PackageManagerService = PackageManagerService = PackageManagerService_1 = __decorate([
(0, tegg_1.SingletonProto)({
accessLevel: tegg_1.AccessLevel.PUBLIC,
})
], PackageManagerService);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGFja2FnZU1hbmFnZXJTZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vYXBwL2NvcmUvc2VydmljZS9QYWNrYWdlTWFuYWdlclNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLCtDQUFrRDtBQUNsRCxzQ0FLcUI7QUFDckIsMkNBQTJEO0FBRTNELHNFQUFrQztBQUNsQyxvREFBNEI7QUFDNUIsMERBT2tDO0FBQ2xDLGtFQUErRDtBQUMvRCwwRUFBeUs7QUFDekssa0dBQStGO0FBQy9GLHdHQUFxRztBQUNyRyxvRUFBaUU7QUFDakUsK0NBQTRDO0FBQzVDLDZEQUEwRDtBQUMxRCx1RUFBb0U7QUFDcEUscURBQWtEO0FBR2xELG9DQVlrQjtBQUNsQiwyREFBd0Q7QUFFeEQscUVBQWtFO0FBRWxFLG1FQUFnRTtBQTJCaEUsTUFBTSxLQUFLLEdBQUcsV0FBVyxDQUFDO0FBQzFCLE1BQU0sa0JBQWtCLEdBQUcsWUFBWSxDQUFDO0FBQ3hDLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxHQUFHLEVBQUUsQ0FBQztBQUs3QixJQUFNLHFCQUFxQiw2QkFBM0IsTUFBTSxxQkFBc0IsU0FBUSxpQ0FBZTtJQW9CeEQsOEVBQThFO0lBQzlFLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBc0IsRUFBRSxTQUFlO1FBQ25ELElBQUksR0FBRyxHQUFHLE1BQU0sSUFBSSxDQUFDLGlCQUFpQixDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN4RSxJQUFJLENBQUMsR0FBRyxFQUFFO1lBQ1IsR0FBRyxHQUFHLGlCQUFPLENBQUMsTUFBTSxDQUFDO2dCQUNuQixLQUFLLEVBQUUsR0FBRyxDQUFDLEtBQUs7Z0JBQ2hCLElBQUksRUFBRSxHQUFHLENBQUMsSUFBSTtnQkFDZCxTQUFTLEVBQUUsR0FBRyxDQUFDLFNBQVM7Z0JBQ3hCLFdBQVcs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