0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2025-03-11 02:15:57 -05:00
verdaccio/packages/store/src/storage-utils.ts
Juan Picado 2ea951d06f
chore: update deps (#2056)
* chore: update deps

* chore: update eslint

* chore: update deps types
2021-01-16 10:40:43 +01:00

256 lines
6.8 KiB
TypeScript

import _ from 'lodash';
import {
ErrorCode,
isObject,
normalizeDistTags,
semverSort,
generateRandomHexString,
isNil,
} from '@verdaccio/utils';
import { Package, Version, Author } from '@verdaccio/types';
import { API_ERROR, HTTP_STATUS, DIST_TAGS, USERS } from '@verdaccio/commons-api';
import { SearchInstance } from './search';
import { IStorage } from './storage';
export const STORAGE = {
PACKAGE_FILE_NAME: 'package.json',
FILE_EXIST_ERROR: 'EEXISTS',
NO_SUCH_FILE_ERROR: 'ENOENT',
DEFAULT_REVISION: '0-0000000000000000',
};
export function generatePackageTemplate(name: string): Package {
return {
// standard things
name,
versions: {},
time: {},
[USERS]: {},
[DIST_TAGS]: {},
_uplinks: {},
_distfiles: {},
_attachments: {},
_rev: '',
};
}
/**
* Normalize package properties, tags, revision id.
* @param {Object} pkg package reference.
*/
export function normalizePackage(pkg: Package): Package {
const pkgProperties = ['versions', 'dist-tags', '_distfiles', '_attachments', '_uplinks', 'time'];
pkgProperties.forEach((key): void => {
const pkgProp = pkg[key];
if (isNil(pkgProp) || isObject(pkgProp) === false) {
pkg[key] = {};
}
});
if (_.isString(pkg._rev) === false) {
pkg._rev = STORAGE.DEFAULT_REVISION;
}
if (_.isString(pkg._id) === false) {
pkg._id = pkg.name;
}
// normalize dist-tags
normalizeDistTags(pkg);
return pkg;
}
export function generateRevision(rev: string): string {
const _rev = rev.split('-');
return (+_rev[0] || 0) + 1 + '-' + generateRandomHexString();
}
export function getLatestReadme(pkg: Package): string {
const versions = pkg['versions'] || {};
const distTags = pkg[DIST_TAGS] || {};
// FIXME: here is a bit tricky add the types
const latestVersion: Version | any = distTags['latest'] ? versions[distTags['latest']] || {} : {};
let readme = _.trim(pkg.readme || latestVersion.readme || '');
if (readme) {
return readme;
}
// In case of empty readme - trying to get ANY readme in the following order:
// 'next','beta','alpha','test','dev','canary'
const readmeDistTagsPriority = ['next', 'beta', 'alpha', 'test', 'dev', 'canary'];
readmeDistTagsPriority.forEach(function (tag): string | void {
if (readme) {
return readme;
}
const version: Version | any = distTags[tag] ? versions[distTags[tag]] || {} : {};
readme = _.trim(version.readme || readme);
});
return readme;
}
// FIXME: type any due this
export function cleanUpReadme(version: any): Version {
if (isNil(version) === false) {
delete version.readme;
}
return version;
}
export function normalizeContributors(contributors: Author[]): Author[] {
if (isNil(contributors)) {
return [];
} else if (contributors && _.isArray(contributors) === false) {
// FIXME: this branch is clearly no an array, still tsc complains
// @ts-ignore
return [contributors];
} else if (_.isString(contributors)) {
return [
{
name: contributors,
},
];
}
return contributors;
}
export const WHITELIST = [
'_rev',
'name',
'versions',
'dist-tags',
'readme',
'time',
'_id',
'users',
];
export function cleanUpLinksRef(keepUpLinkData: boolean, result: Package): Package {
const propertyToKeep = [...WHITELIST];
if (keepUpLinkData === true) {
propertyToKeep.push('_uplinks');
}
for (const i in result) {
if (propertyToKeep.indexOf(i) === -1) {
// Remove sections like '_uplinks' from response
delete result[i];
}
}
return result;
}
/**
* Check whether a package it is already a local package
* @param {*} name
* @param {*} localStorage
*/
export function checkPackageLocal(name: string, localStorage: IStorage): Promise<any> {
return new Promise<void>((resolve, reject): void => {
localStorage.getPackageMetadata(name, (err, results): void => {
if (!isNil(err) && err.status !== HTTP_STATUS.NOT_FOUND) {
return reject(err);
}
if (results) {
return reject(ErrorCode.getConflict(API_ERROR.PACKAGE_EXIST));
}
return resolve();
});
});
}
export function publishPackage(name: string, metadata: any, localStorage: IStorage): Promise<any> {
return new Promise<void>((resolve, reject): void => {
localStorage.addPackage(name, metadata, (err, latest): void => {
if (!_.isNull(err)) {
return reject(err);
} else if (!_.isUndefined(latest)) {
SearchInstance.add(latest);
}
return resolve();
});
});
}
export function checkPackageRemote(
name: string,
isAllowPublishOffline: boolean,
syncMetadata: Function
): Promise<any> {
return new Promise<void>((resolve, reject): void => {
syncMetadata(name, null, {}, (err, packageJsonLocal, upLinksErrors): void => {
// something weird
if (err && err.status !== HTTP_STATUS.NOT_FOUND) {
return reject(err);
}
// checking package exist already
if (isNil(packageJsonLocal) === false) {
return reject(ErrorCode.getConflict(API_ERROR.PACKAGE_EXIST));
}
for (let errorItem = 0; errorItem < upLinksErrors.length; errorItem++) {
// checking error
// if uplink fails with a status other than 404, we report failure
if (isNil(upLinksErrors[errorItem][0]) === false) {
if (upLinksErrors[errorItem][0].status !== HTTP_STATUS.NOT_FOUND) {
if (isAllowPublishOffline) {
return resolve();
}
return reject(ErrorCode.getServiceUnavailable(API_ERROR.UPLINK_OFFLINE_PUBLISH));
}
}
}
return resolve();
});
});
}
export function mergeUplinkTimeIntoLocal(localMetadata: Package, remoteMetadata: Package): any {
if ('time' in remoteMetadata) {
return Object.assign({}, localMetadata.time, remoteMetadata.time);
}
return localMetadata.time;
}
export function prepareSearchPackage(data: Package, time: unknown): any {
const listVersions: string[] = Object.keys(data.versions);
const versions: string[] = semverSort(listVersions);
const latest: string | undefined =
data[DIST_TAGS] && data[DIST_TAGS].latest ? data[DIST_TAGS].latest : versions.pop();
if (latest && data.versions[latest]) {
const version: Version = data.versions[latest];
const versions: any = { [latest]: 'latest' };
const pkg: any = {
name: version.name,
description: version.description,
[DIST_TAGS]: { latest },
maintainers: version.maintainers || [version.author].filter(Boolean),
author: version.author,
repository: version.repository,
readmeFilename: version.readmeFilename || '',
homepage: version.homepage,
keywords: version.keywords,
bugs: version.bugs,
license: version.license,
time: {
modified: time,
},
versions,
};
return pkg;
}
}