cnpmcore
Version:
345 lines • 35.4 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 __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _PackageVersionFileService_instances, _PackageVersionFileService_unpkgWhiteListCheckTime, _PackageVersionFileService_unpkgWhiteListCurrentVersion, _PackageVersionFileService_unpkgWhiteListAllowPackages, _PackageVersionFileService_unpkgWhiteListAllowScopes, _PackageVersionFileService_ensurePackageVersionFilesSync, _PackageVersionFileService_updateUnpkgWhiteList, _PackageVersionFileService_savePackageVersionFile, _PackageVersionFileService_getDirectoryAndName, _PackageVersionFileService_formatTarEntryFilename, _PackageVersionFileService_matchReadmeFilename, _PackageVersionFileService_preferMarkdownReadme;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PackageVersionFileService = void 0;
const promises_1 = __importDefault(require("node:fs/promises"));
const node_path_1 = require("node:path");
const node_crypto_1 = require("node:crypto");
const tar_1 = __importDefault(require("@fengmk2/tar"));
const tegg_1 = require("@eggjs/tegg");
const egg_errors_1 = require("egg-errors");
const semver_1 = __importDefault(require("semver"));
const AbstractService_1 = require("../../common/AbstractService");
const PackageUtil_1 = require("../../common/PackageUtil");
const FileUtil_1 = require("../../common/FileUtil");
const PackageRepository_1 = require("../../repository/PackageRepository");
const PackageVersionFileRepository_1 = require("../../repository/PackageVersionFileRepository");
const PackageVersionRepository_1 = require("../../repository/PackageVersionRepository");
const DistRepository_1 = require("../../repository/DistRepository");
const PackageVersionFile_1 = require("../entity/PackageVersionFile");
const PackageManagerService_1 = require("./PackageManagerService");
const CacheAdapter_1 = require("../../common/adapter/CacheAdapter");
const unpkgWhiteListUrl = 'https://github.com/cnpm/unpkg-white-list';
const CHECK_TIMEOUT = process.env.NODE_ENV === 'test' ? 1 : 60000;
let PackageVersionFileService = class PackageVersionFileService extends AbstractService_1.AbstractService {
constructor() {
super(...arguments);
_PackageVersionFileService_instances.add(this);
_PackageVersionFileService_unpkgWhiteListCheckTime.set(this, 0);
_PackageVersionFileService_unpkgWhiteListCurrentVersion.set(this, '');
_PackageVersionFileService_unpkgWhiteListAllowPackages.set(this, {});
_PackageVersionFileService_unpkgWhiteListAllowScopes.set(this, []);
}
async listPackageVersionFiles(pkgVersion, directory) {
await __classPrivateFieldGet(this, _PackageVersionFileService_instances, "m", _PackageVersionFileService_ensurePackageVersionFilesSync).call(this, pkgVersion);
return await this.packageVersionFileRepository.listPackageVersionFiles(pkgVersion.packageVersionId, directory);
}
async showPackageVersionFile(pkgVersion, path) {
await __classPrivateFieldGet(this, _PackageVersionFileService_instances, "m", _PackageVersionFileService_ensurePackageVersionFilesSync).call(this, pkgVersion);
const { directory, name } = __classPrivateFieldGet(this, _PackageVersionFileService_instances, "m", _PackageVersionFileService_getDirectoryAndName).call(this, path);
return await this.packageVersionFileRepository.findPackageVersionFile(pkgVersion.packageVersionId, directory, name);
}
async checkPackageVersionInUnpkgWhiteList(pkgScope, pkgName, pkgVersion) {
if (!this.config.cnpmcore.enableSyncUnpkgFilesWhiteList)
return;
await __classPrivateFieldGet(this, _PackageVersionFileService_instances, "m", _PackageVersionFileService_updateUnpkgWhiteList).call(this);
// check allow scopes
if (__classPrivateFieldGet(this, _PackageVersionFileService_unpkgWhiteListAllowScopes, "f").includes(pkgScope))
return;
// check allow packages
const fullname = (0, PackageUtil_1.getFullname)(pkgScope, pkgName);
const pkgConfig = __classPrivateFieldGet(this, _PackageVersionFileService_unpkgWhiteListAllowPackages, "f")[fullname];
if (!pkgConfig?.version) {
throw new egg_errors_1.ForbiddenError(`"${fullname}" is not allow to unpkg files, see ${unpkgWhiteListUrl}`);
}
// satisfies 默认不会包含 prerelease 版本
// https://docs.npmjs.com/about-semantic-versioning#using-semantic-versioning-to-specify-update-types-your-package-can-accept
// [x, *] 代表任意版本,这里统一通过 semver 来判断
if (!semver_1.default.satisfies(pkgVersion, pkgConfig.version, { includePrerelease: true })) {
throw new egg_errors_1.ForbiddenError(`"${fullname}@${pkgVersion}" not satisfies "${pkgConfig.version}" to unpkg files, see ${unpkgWhiteListUrl}`);
}
}
// 基于 latest version 同步 package readme
async syncPackageReadme(pkg, latestPkgVersion) {
const dirname = `unpkg_${pkg.fullname.replace('/', '_')}@${latestPkgVersion.version}_latest_readme_${(0, node_crypto_1.randomUUID)()}`;
const tmpdir = await (0, FileUtil_1.createTempDir)(this.config.dataDir, dirname);
const tarFile = `${tmpdir}.tgz`;
const readmeFilenames = [];
try {
this.logger.info('[PackageVersionFileService.syncPackageReadme:download-start] dist:%s(path:%s, size:%s) => tarFile:%s', latestPkgVersion.tarDist.distId, latestPkgVersion.tarDist.path, latestPkgVersion.tarDist.size, tarFile);
await this.distRepository.downloadDistToFile(latestPkgVersion.tarDist, tarFile);
this.logger.info('[PackageVersionFileService.syncPackageReadme:extract-start] tmpdir:%s', tmpdir);
await tar_1.default.extract({
file: tarFile,
cwd: tmpdir,
strip: 1,
onentry: entry => {
const filename = __classPrivateFieldGet(this, _PackageVersionFileService_instances, "m", _PackageVersionFileService_formatTarEntryFilename).call(this, entry);
if (!filename)
return;
if (__classPrivateFieldGet(this, _PackageVersionFileService_instances, "m", _PackageVersionFileService_matchReadmeFilename).call(this, filename)) {
readmeFilenames.push(filename);
}
},
});
if (readmeFilenames.length > 0) {
const readmeFilename = __classPrivateFieldGet(this, _PackageVersionFileService_instances, "m", _PackageVersionFileService_preferMarkdownReadme).call(this, readmeFilenames);
const readmeFile = (0, node_path_1.join)(tmpdir, readmeFilename);
await this.packageManagerService.savePackageReadme(pkg, readmeFile);
}
}
catch (err) {
this.logger.warn('[PackageVersionFileService.syncPackageReadme:error] packageVersionId: %s, readmeFilenames: %j, tmpdir: %s, error: %s', latestPkgVersion.packageVersionId, readmeFilenames, tmpdir, err);
// ignore TAR_BAD_ARCHIVE error
if (err.code === 'TAR_BAD_ARCHIVE')
return;
throw err;
}
finally {
try {
await promises_1.default.rm(tarFile, { force: true });
await promises_1.default.rm(tmpdir, { recursive: true, force: true });
}
catch (err) {
this.logger.warn('[PackageVersionFileService.syncPackageReadme:warn] remove tmpdir: %s, error: %s', tmpdir, err);
}
}
}
async syncPackageVersionFiles(pkgVersion) {
const files = [];
// must set enableUnpkg and enableSyncUnpkgFiles = true both
if (!this.config.cnpmcore.enableUnpkg)
return files;
if (!this.config.cnpmcore.enableSyncUnpkgFiles)
return files;
const pkg = await this.packageRepository.findPackageByPackageId(pkgVersion.packageId);
if (!pkg)
return files;
// check unpkg white list
await this.checkPackageVersionInUnpkgWhiteList(pkg.scope, pkg.name, pkgVersion.version);
const dirname = `unpkg_${pkg.fullname.replace('/', '_')}@${pkgVersion.version}_${(0, node_crypto_1.randomUUID)()}`;
const tmpdir = await (0, FileUtil_1.createTempDir)(this.config.dataDir, dirname);
const tarFile = `${tmpdir}.tgz`;
const paths = [];
const readmeFilenames = [];
try {
this.logger.info('[PackageVersionFileService.syncPackageVersionFiles:download-start] dist:%s(path:%s, size:%s) => tarFile:%s', pkgVersion.tarDist.distId, pkgVersion.tarDist.path, pkgVersion.tarDist.size, tarFile);
await this.distRepository.downloadDistToFile(pkgVersion.tarDist, tarFile);
this.logger.info('[PackageVersionFileService.syncPackageVersionFiles:extract-start] tmpdir:%s', tmpdir);
await tar_1.default.extract({
file: tarFile,
cwd: tmpdir,
strip: 1,
onentry: entry => {
const filename = __classPrivateFieldGet(this, _PackageVersionFileService_instances, "m", _PackageVersionFileService_formatTarEntryFilename).call(this, entry);
if (!filename)
return;
paths.push('/' + filename);
if (__classPrivateFieldGet(this, _PackageVersionFileService_instances, "m", _PackageVersionFileService_matchReadmeFilename).call(this, filename)) {
readmeFilenames.push(filename);
}
},
});
for (const path of paths) {
const localFile = (0, node_path_1.join)(tmpdir, path);
const file = await __classPrivateFieldGet(this, _PackageVersionFileService_instances, "m", _PackageVersionFileService_savePackageVersionFile).call(this, pkg, pkgVersion, path, localFile);
files.push(file);
}
this.logger.info('[PackageVersionFileService.syncPackageVersionFiles:success] packageVersionId: %s, %d paths, %d files, tmpdir: %s', pkgVersion.packageVersionId, paths.length, files.length, tmpdir);
if (readmeFilenames.length > 0) {
const readmeFilename = __classPrivateFieldGet(this, _PackageVersionFileService_instances, "m", _PackageVersionFileService_preferMarkdownReadme).call(this, readmeFilenames);
const readmeFile = (0, node_path_1.join)(tmpdir, readmeFilename);
await this.packageManagerService.savePackageVersionReadme(pkgVersion, readmeFile);
}
return files;
}
catch (err) {
this.logger.warn('[PackageVersionFileService.syncPackageVersionFiles:error] packageVersionId: %s, %d paths, tmpdir: %s, error: %s', pkgVersion.packageVersionId, paths.length, tmpdir, err);
// ignore TAR_BAD_ARCHIVE error
if (err.code === 'TAR_BAD_ARCHIVE')
return files;
throw err;
}
finally {
try {
await promises_1.default.rm(tarFile, { force: true });
await promises_1.default.rm(tmpdir, { recursive: true, force: true });
}
catch (err) {
this.logger.warn('[PackageVersionFileService.syncPackageVersionFiles:warn] remove tmpdir: %s, error: %s', tmpdir, err);
}
}
}
};
exports.PackageVersionFileService = PackageVersionFileService;
_PackageVersionFileService_unpkgWhiteListCheckTime = new WeakMap();
_PackageVersionFileService_unpkgWhiteListCurrentVersion = new WeakMap();
_PackageVersionFileService_unpkgWhiteListAllowPackages = new WeakMap();
_PackageVersionFileService_unpkgWhiteListAllowScopes = new WeakMap();
_PackageVersionFileService_instances = new WeakSet();
_PackageVersionFileService_ensurePackageVersionFilesSync = async function _PackageVersionFileService_ensurePackageVersionFilesSync(pkgVersion) {
const hasFiles = await this.packageVersionFileRepository.hasPackageVersionFiles(pkgVersion.packageVersionId);
if (!hasFiles) {
const lockName = `${pkgVersion.packageVersionId}:syncFiles`;
const lockRes = await this.cacheAdapter.usingLock(lockName, 60, async () => {
await this.syncPackageVersionFiles(pkgVersion);
});
// lock fail
if (!lockRes) {
this.logger.warn('[package:version:syncPackageVersionFiles] check lock:%s fail', lockName);
throw new egg_errors_1.ConflictError('Package version file sync is currently in progress. Please try again later.');
}
}
};
_PackageVersionFileService_updateUnpkgWhiteList = async function _PackageVersionFileService_updateUnpkgWhiteList() {
if (!this.config.cnpmcore.enableSyncUnpkgFilesWhiteList)
return;
if (Date.now() - __classPrivateFieldGet(this, _PackageVersionFileService_unpkgWhiteListCheckTime, "f") <= CHECK_TIMEOUT) {
// check update every 60s
return;
}
__classPrivateFieldSet(this, _PackageVersionFileService_unpkgWhiteListCheckTime, Date.now(), "f");
const whiteListScope = '';
const whiteListPackageName = 'unpkg-white-list';
const whiteListPackageVersion = await this.packageVersionRepository.findVersionByTag(whiteListScope, whiteListPackageName, 'latest');
if (!whiteListPackageVersion)
return;
// same version, skip update for performance
if (__classPrivateFieldGet(this, _PackageVersionFileService_unpkgWhiteListCurrentVersion, "f") === whiteListPackageVersion)
return;
// update the new version white list
const { manifest } = await this.packageManagerService.showPackageVersionManifest(whiteListScope, whiteListPackageName, whiteListPackageVersion, false, true);
if (!manifest)
return;
__classPrivateFieldSet(this, _PackageVersionFileService_unpkgWhiteListCurrentVersion, manifest.version, "f");
__classPrivateFieldSet(this, _PackageVersionFileService_unpkgWhiteListAllowPackages, manifest.allowPackages ?? {}, "f");
__classPrivateFieldSet(this, _PackageVersionFileService_unpkgWhiteListAllowScopes, manifest.allowScopes ?? [], "f");
this.logger.info('[PackageVersionFileService.updateUnpkgWhiteList] version:%s, total %s packages, %s scopes', whiteListPackageVersion, Object.keys(__classPrivateFieldGet(this, _PackageVersionFileService_unpkgWhiteListAllowPackages, "f")).length, __classPrivateFieldGet(this, _PackageVersionFileService_unpkgWhiteListAllowScopes, "f").length);
};
_PackageVersionFileService_savePackageVersionFile = async function _PackageVersionFileService_savePackageVersionFile(pkg, pkgVersion, path, localFile) {
const { directory, name } = __classPrivateFieldGet(this, _PackageVersionFileService_instances, "m", _PackageVersionFileService_getDirectoryAndName).call(this, path);
let file = await this.packageVersionFileRepository.findPackageVersionFile(pkgVersion.packageVersionId, directory, name);
if (file)
return file;
const stat = await promises_1.default.stat(localFile);
const distIntegrity = await (0, PackageUtil_1.calculateIntegrity)(localFile);
// make sure dist.path store to ascii, e.g. '/resource/ToOneFromχ.js' => '/resource/ToOneFrom%CF%87.js'
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI
const distPath = encodeURI(path);
const dist = pkg.createPackageVersionFile(distPath, pkgVersion.version, {
size: stat.size,
shasum: distIntegrity.shasum,
integrity: distIntegrity.integrity,
});
await this.distRepository.saveDist(dist, localFile);
file = PackageVersionFile_1.PackageVersionFile.create({
packageVersionId: pkgVersion.packageVersionId,
directory,
name,
dist,
contentType: (0, FileUtil_1.mimeLookup)(path),
mtime: pkgVersion.publishTime,
});
try {
await this.packageVersionFileRepository.createPackageVersionFile(file);
this.logger.info('[PackageVersionFileService.#savePackageVersionFile:success] fileId: %s, size: %s, path: %s', file.packageVersionFileId, dist.size, file.path);
}
catch (err) {
// ignore Duplicate entry
if (err.code === 'ER_DUP_ENTRY')
return file;
throw err;
}
return file;
};
_PackageVersionFileService_getDirectoryAndName = function _PackageVersionFileService_getDirectoryAndName(path) {
return {
directory: (0, node_path_1.dirname)(path),
name: (0, node_path_1.basename)(path),
};
};
_PackageVersionFileService_formatTarEntryFilename = function _PackageVersionFileService_formatTarEntryFilename(entry) {
if (entry.type !== 'File')
return;
// ignore hidden dir
if (entry.path.includes('/./'))
return;
// https://github.com/cnpm/cnpmcore/issues/452#issuecomment-1570077310
// strip first dir, e.g.: 'package/', 'lodash-es/'
const filename = entry.path.split('/').slice(1).join('/');
return filename;
};
_PackageVersionFileService_matchReadmeFilename = function _PackageVersionFileService_matchReadmeFilename(filename) {
// support README,README.*
// https://github.com/npm/read-package-json/blob/main/lib/read-json.js#L280
return (/^README(\.\w{1,20}|$)/i.test(filename));
};
_PackageVersionFileService_preferMarkdownReadme = function _PackageVersionFileService_preferMarkdownReadme(files) {
let fallback = 0;
const markdownRE = /\.m?a?r?k?d?o?w?n?$/i;
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (markdownRE.test(file)) {
return file;
}
else if (file.toLowerCase() === 'README') {
fallback = i;
}
}
// prefer README.md, followed by README; otherwise, return
// the first filename (which could be README)
return files[fallback];
};
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", PackageVersionRepository_1.PackageVersionRepository)
], PackageVersionFileService.prototype, "packageVersionRepository", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", PackageRepository_1.PackageRepository)
], PackageVersionFileService.prototype, "packageRepository", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", PackageVersionFileRepository_1.PackageVersionFileRepository)
], PackageVersionFileService.prototype, "packageVersionFileRepository", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", DistRepository_1.DistRepository)
], PackageVersionFileService.prototype, "distRepository", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", PackageManagerService_1.PackageManagerService)
], PackageVersionFileService.prototype, "packageManagerService", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", CacheAdapter_1.CacheAdapter)
], PackageVersionFileService.prototype, "cacheAdapter", void 0);
exports.PackageVersionFileService = PackageVersionFileService = __decorate([
(0, tegg_1.SingletonProto)({
accessLevel: tegg_1.AccessLevel.PUBLIC,
})
], PackageVersionFileService);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGFja2FnZVZlcnNpb25GaWxlU2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2FwcC9jb3JlL3NlcnZpY2UvUGFja2FnZVZlcnNpb25GaWxlU2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxnRUFBa0M7QUFDbEMseUNBQW9EO0FBQ3BELDZDQUF5QztBQUN6Qyx1REFBK0I7QUFDL0Isc0NBSXFCO0FBQ3JCLDJDQUEyRDtBQUMzRCxvREFBNEI7QUFDNUIsa0VBQStEO0FBQy9ELDBEQUdrQztBQUNsQyxvREFBa0U7QUFDbEUsMEVBRTRDO0FBQzVDLGdHQUE2RjtBQUM3Rix3RkFBcUY7QUFDckYsb0VBQWlFO0FBQ2pFLHFFQUFrRTtBQUdsRSxtRUFBZ0U7QUFDaEUsb0VBQWlFO0FBRWpFLE1BQU0saUJBQWlCLEdBQUcsMENBQTBDLENBQUM7QUFDckUsTUFBTSxhQUFhLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRLEtBQUssTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQztBQUszRCxJQUFNLHlCQUF5QixHQUEvQixNQUFNLHlCQUEwQixTQUFRLGlDQUFlO0lBQXZEOzs7UUFjTCw2REFBbUMsQ0FBQyxFQUFDO1FBQ3JDLGtFQUF3QyxFQUFFLEVBQUM7UUFDM0MsaUVBRUssRUFBRSxFQUFDO1FBQ1IsK0RBQXVDLEVBQUUsRUFBQztJQXlRNUMsQ0FBQztJQXZRQyxLQUFLLENBQUMsdUJBQXVCLENBQUMsVUFBMEIsRUFBRSxTQUFpQjtRQUN6RSxNQUFNLHVCQUFBLElBQUksc0dBQStCLE1BQW5DLElBQUksRUFBZ0MsVUFBVSxDQUFDLENBQUM7UUFDdEQsT0FBTyxNQUFNLElBQUksQ0FBQyw0QkFBNEIsQ0FBQyx1QkFBdUIsQ0FBQyxVQUFVLENBQUMsZ0JBQWdCLEVBQUUsU0FBUyxDQUFDLENBQUM7SUFDakgs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