@verdaccio/store 9.0.0-next-9.30 → 9.0.0-next-9.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.js +4 -0
- package/build/index.mjs +2 -2
- package/build/lib/storage-utils.d.ts +33 -2
- package/build/lib/storage-utils.js +87 -10
- package/build/lib/storage-utils.js.map +1 -1
- package/build/lib/storage-utils.mjs +83 -11
- package/build/lib/storage-utils.mjs.map +1 -1
- package/build/lib/versions-utils.d.ts +5 -5
- package/build/lib/versions-utils.js +13 -8
- package/build/lib/versions-utils.js.map +1 -1
- package/build/lib/versions-utils.mjs +14 -9
- package/build/lib/versions-utils.mjs.map +1 -1
- package/build/storage.d.ts +12 -0
- package/build/storage.js +117 -19
- package/build/storage.js.map +1 -1
- package/build/storage.mjs +121 -23
- package/build/storage.mjs.map +1 -1
- package/package.json +14 -14
package/build/index.js
CHANGED
|
@@ -17,6 +17,7 @@ exports.applyManifestFilters = require_filter_pipeline.applyManifestFilters;
|
|
|
17
17
|
exports.checkPackageRemote = require_storage_utils.checkPackageRemote;
|
|
18
18
|
exports.cleanUpLinksRef = require_storage_utils.cleanUpLinksRef;
|
|
19
19
|
exports.cleanUpReadme = require_storage_utils.cleanUpReadme;
|
|
20
|
+
exports.distFileFromVersion = require_storage_utils.distFileFromVersion;
|
|
20
21
|
exports.generatePackageTemplate = require_storage_utils.generatePackageTemplate;
|
|
21
22
|
exports.generateRevision = require_storage_utils.generateRevision;
|
|
22
23
|
exports.getLatestReadme = require_storage_utils.getLatestReadme;
|
|
@@ -26,6 +27,7 @@ exports.isDeprecatedManifest = require_storage_utils.isDeprecatedManifest;
|
|
|
26
27
|
exports.isNewerVersion = require_versions_utils.isNewerVersion;
|
|
27
28
|
exports.isPublishablePackage = require_publish_utils.isPublishablePackage;
|
|
28
29
|
exports.loadFilterPlugins = require_filter_pipeline.loadFilterPlugins;
|
|
30
|
+
exports.lookupDistFile = require_storage_utils.lookupDistFile;
|
|
29
31
|
exports.mapManifestToSearchPackageBody = require_storage_utils.mapManifestToSearchPackageBody;
|
|
30
32
|
exports.mergeUplinkTimeIntoLocal = require_storage_utils.mergeUplinkTimeIntoLocal;
|
|
31
33
|
exports.mergeUplinkTimeIntoLocalNext = require_storage_utils.mergeUplinkTimeIntoLocalNext;
|
|
@@ -38,5 +40,7 @@ exports.removeLowerVersions = require_versions_utils.removeLowerVersions;
|
|
|
38
40
|
exports.sortVersionsAndFilterInvalid = require_versions_utils.sortVersionsAndFilterInvalid;
|
|
39
41
|
exports.tagVersion = require_versions_utils.tagVersion;
|
|
40
42
|
exports.tagVersionNext = require_versions_utils.tagVersionNext;
|
|
43
|
+
exports.tarballMatchesFilename = require_storage_utils.tarballMatchesFilename;
|
|
41
44
|
exports.toStagePackageVersion = require_stage_storage.toStagePackageVersion;
|
|
42
45
|
exports.updateUpLinkMetadata = require_storage_utils.updateUpLinkMetadata;
|
|
46
|
+
exports.uplinkServesUrl = require_storage_utils.uplinkServesUrl;
|
package/build/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { applyManifestFilters, loadFilterPlugins } from "./filter-pipeline.mjs";
|
|
2
2
|
import { isPublishablePackage } from "./lib/publish-utils.mjs";
|
|
3
3
|
import { getVersion, isNewerVersion, removeLowerVersions, sortVersionsAndFilterInvalid, tagVersion, tagVersionNext } from "./lib/versions-utils.mjs";
|
|
4
|
-
import { STORAGE, WHITELIST, checkPackageRemote, cleanUpLinksRef, cleanUpReadme, generatePackageTemplate, generateRevision, getLatestReadme, hasDeprecatedVersions, isDeprecatedManifest, mapManifestToSearchPackageBody, mergeUplinkTimeIntoLocal, mergeUplinkTimeIntoLocalNext, mergeVersions, normalizeContributors, normalizeDistTags, normalizePackage, prepareSearchPackage, updateUpLinkMetadata } from "./lib/storage-utils.mjs";
|
|
4
|
+
import { STORAGE, WHITELIST, checkPackageRemote, cleanUpLinksRef, cleanUpReadme, distFileFromVersion, generatePackageTemplate, generateRevision, getLatestReadme, hasDeprecatedVersions, isDeprecatedManifest, lookupDistFile, mapManifestToSearchPackageBody, mergeUplinkTimeIntoLocal, mergeUplinkTimeIntoLocalNext, mergeVersions, normalizeContributors, normalizeDistTags, normalizePackage, prepareSearchPackage, tarballMatchesFilename, updateUpLinkMetadata, uplinkServesUrl } from "./lib/storage-utils.mjs";
|
|
5
5
|
import { STAGE_NAMESPACE, StageStorage, toStagePackageVersion } from "./stage-storage.mjs";
|
|
6
6
|
import { Storage } from "./storage.mjs";
|
|
7
|
-
export { STAGE_NAMESPACE, STORAGE, StageStorage, Storage, WHITELIST, applyManifestFilters, checkPackageRemote, cleanUpLinksRef, cleanUpReadme, generatePackageTemplate, generateRevision, getLatestReadme, getVersion, hasDeprecatedVersions, isDeprecatedManifest, isNewerVersion, isPublishablePackage, loadFilterPlugins, mapManifestToSearchPackageBody, mergeUplinkTimeIntoLocal, mergeUplinkTimeIntoLocalNext, mergeVersions, normalizeContributors, normalizeDistTags, normalizePackage, prepareSearchPackage, removeLowerVersions, sortVersionsAndFilterInvalid, tagVersion, tagVersionNext, toStagePackageVersion, updateUpLinkMetadata };
|
|
7
|
+
export { STAGE_NAMESPACE, STORAGE, StageStorage, Storage, WHITELIST, applyManifestFilters, checkPackageRemote, cleanUpLinksRef, cleanUpReadme, distFileFromVersion, generatePackageTemplate, generateRevision, getLatestReadme, getVersion, hasDeprecatedVersions, isDeprecatedManifest, isNewerVersion, isPublishablePackage, loadFilterPlugins, lookupDistFile, mapManifestToSearchPackageBody, mergeUplinkTimeIntoLocal, mergeUplinkTimeIntoLocalNext, mergeVersions, normalizeContributors, normalizeDistTags, normalizePackage, prepareSearchPackage, removeLowerVersions, sortVersionsAndFilterInvalid, tagVersion, tagVersionNext, tarballMatchesFilename, toStagePackageVersion, updateUpLinkMetadata, uplinkServesUrl };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { searchUtils } from '@verdaccio/core';
|
|
2
|
-
import type { Author, GenericBody, Manifest, ReadmeOptions, Version } from '@verdaccio/types';
|
|
2
|
+
import type { Author, DistFile, GenericBody, Manifest, ReadmeOptions, Version } from '@verdaccio/types';
|
|
3
3
|
export declare const STORAGE: {
|
|
4
4
|
PACKAGE_FILE_NAME: string;
|
|
5
5
|
FILE_EXIST_ERROR: string;
|
|
@@ -95,5 +95,36 @@ export declare function mergeVersions(cacheManifest: Manifest, remoteManifest: M
|
|
|
95
95
|
export declare function normalizeDistTags(manifest: Manifest): Manifest;
|
|
96
96
|
export declare function hasDeprecatedVersions(pkgInfo: Manifest): boolean;
|
|
97
97
|
export declare function isDeprecatedManifest(manifest: Manifest): boolean;
|
|
98
|
-
export declare function mapManifestToSearchPackageBody(pkg: Manifest,
|
|
98
|
+
export declare function mapManifestToSearchPackageBody(pkg: Manifest, _searchItem: searchUtils.SearchItem): searchUtils.SearchPackageBody;
|
|
99
99
|
export declare function normalizeContributors(contributors: Author[]): Author[];
|
|
100
|
+
/**
|
|
101
|
+
* Whether the tarball url path ends with the given file name (any query
|
|
102
|
+
* string or fragment is ignored, like the path parsing in
|
|
103
|
+
* `tarballUtils.extractTarballFromUrl`, which creates the `_distfiles`
|
|
104
|
+
* keys — if that parsing ever changes, this must change with it).
|
|
105
|
+
*/
|
|
106
|
+
export declare function tarballMatchesFilename(tarball: string, filename: string): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Build a distfile record from a version's dist metadata when its tarball
|
|
109
|
+
* url matches the given file name.
|
|
110
|
+
*/
|
|
111
|
+
export declare function distFileFromVersion(version: Version | undefined, filename: string): DistFile | null;
|
|
112
|
+
/**
|
|
113
|
+
* Resolve the distfile record for a tarball filename. Prefers the
|
|
114
|
+
* `_distfiles` bookkeeping, but falls back to the version metadata when the
|
|
115
|
+
* record is missing (storages written by other verdaccio versions cache
|
|
116
|
+
* versions without their distfile records, which made those tarballs
|
|
117
|
+
* permanently unavailable).
|
|
118
|
+
*
|
|
119
|
+
* Note: no extension pre-filter before the scan on purpose — GitHub
|
|
120
|
+
* Packages advertises tarball urls ending in a bare sha-256 digest.
|
|
121
|
+
* @param manifest cached manifest
|
|
122
|
+
* @param filename tarball file name, eg. pkg-1.0.0.tgz
|
|
123
|
+
*/
|
|
124
|
+
export declare function lookupDistFile(manifest: Manifest, filename: string): DistFile | null;
|
|
125
|
+
/**
|
|
126
|
+
* Whether an uplink's configured url serves the given tarball url: same
|
|
127
|
+
* protocol, same host and the uplink path is a prefix of the tarball path.
|
|
128
|
+
* WHATWG URL normalizes default ports (https://h:443 === https://h).
|
|
129
|
+
*/
|
|
130
|
+
export declare function uplinkServesUrl(uplinkUrl: URL, tarballUrl: string): boolean;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const require_runtime = require("../_virtual/_rolldown/runtime.js");
|
|
2
2
|
const require_versions_utils = require("./versions-utils.js");
|
|
3
|
+
let debug = require("debug");
|
|
4
|
+
debug = require_runtime.__toESM(debug);
|
|
3
5
|
let lodash_es = require("lodash-es");
|
|
4
6
|
let _verdaccio_core = require("@verdaccio/core");
|
|
5
7
|
let semver = require("semver");
|
|
6
8
|
semver = require_runtime.__toESM(semver);
|
|
7
9
|
//#region src/lib/storage-utils.ts
|
|
10
|
+
var debug$1 = (0, debug.default)("verdaccio:storage:utils");
|
|
8
11
|
var STORAGE = {
|
|
9
12
|
PACKAGE_FILE_NAME: "package.json",
|
|
10
13
|
FILE_EXIST_ERROR: "EEXISTS",
|
|
@@ -228,7 +231,7 @@ function hasDeprecatedVersions(pkgInfo) {
|
|
|
228
231
|
function isDeprecatedManifest(manifest) {
|
|
229
232
|
return hasDeprecatedVersions(manifest) && (typeof manifest._attachments === "undefined" || Object.keys(manifest._attachments).length === 0);
|
|
230
233
|
}
|
|
231
|
-
function mapManifestToSearchPackageBody(pkg,
|
|
234
|
+
function mapManifestToSearchPackageBody(pkg, _searchItem) {
|
|
232
235
|
const latest = _verdaccio_core.pkgUtils.getLatest(pkg);
|
|
233
236
|
const version = pkg.versions[latest];
|
|
234
237
|
const maintainers = Array.isArray(version.maintainers) ? version.maintainers.map((maintainer) => typeof maintainer === "string" ? {
|
|
@@ -243,24 +246,24 @@ function mapManifestToSearchPackageBody(pkg, searchItem) {
|
|
|
243
246
|
username: version._npmUser.name,
|
|
244
247
|
email: version._npmUser.email ?? ""
|
|
245
248
|
} : maintainers[0] ?? {};
|
|
249
|
+
const links = {};
|
|
250
|
+
const repository = typeof version.repository === "string" ? version.repository : version.repository?.url;
|
|
251
|
+
const bugs = typeof version.bugs === "string" ? version.bugs : version.bugs?.url;
|
|
252
|
+
if (version.homepage) links.homepage = version.homepage;
|
|
253
|
+
if (repository) links.repository = repository;
|
|
254
|
+
if (bugs) links.bugs = bugs;
|
|
246
255
|
const result = {
|
|
247
256
|
name: version.name,
|
|
248
|
-
scope: "",
|
|
249
257
|
description: version.description,
|
|
250
258
|
version: latest,
|
|
259
|
+
license: version.license,
|
|
251
260
|
keywords: version.keywords,
|
|
252
261
|
date: pkg.time[latest],
|
|
253
262
|
author: version.author,
|
|
254
263
|
publisher,
|
|
255
|
-
maintainers
|
|
256
|
-
links: {
|
|
257
|
-
npm: "",
|
|
258
|
-
homepage: version.homepage,
|
|
259
|
-
repository: version.repository,
|
|
260
|
-
bugs: version.bugs
|
|
261
|
-
}
|
|
264
|
+
maintainers
|
|
262
265
|
};
|
|
263
|
-
if (
|
|
266
|
+
if (Object.keys(links).length > 0) result.links = links;
|
|
264
267
|
return result;
|
|
265
268
|
}
|
|
266
269
|
function normalizeContributors(contributors) {
|
|
@@ -269,17 +272,89 @@ function normalizeContributors(contributors) {
|
|
|
269
272
|
else if (typeof contributors === "string") return [{ name: contributors }];
|
|
270
273
|
return contributors;
|
|
271
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Whether the tarball url path ends with the given file name (any query
|
|
277
|
+
* string or fragment is ignored, like the path parsing in
|
|
278
|
+
* `tarballUtils.extractTarballFromUrl`, which creates the `_distfiles`
|
|
279
|
+
* keys — if that parsing ever changes, this must change with it).
|
|
280
|
+
*/
|
|
281
|
+
function tarballMatchesFilename(tarball, filename) {
|
|
282
|
+
return tarball.split(/[?#]/)[0].endsWith(`/${filename}`);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Build a distfile record from a version's dist metadata when its tarball
|
|
286
|
+
* url matches the given file name.
|
|
287
|
+
*/
|
|
288
|
+
function distFileFromVersion(version, filename) {
|
|
289
|
+
const dist = version?.dist;
|
|
290
|
+
if (dist?.tarball && tarballMatchesFilename(dist.tarball, filename)) return {
|
|
291
|
+
url: dist.tarball,
|
|
292
|
+
sha: dist.shasum
|
|
293
|
+
};
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Resolve the distfile record for a tarball filename. Prefers the
|
|
298
|
+
* `_distfiles` bookkeeping, but falls back to the version metadata when the
|
|
299
|
+
* record is missing (storages written by other verdaccio versions cache
|
|
300
|
+
* versions without their distfile records, which made those tarballs
|
|
301
|
+
* permanently unavailable).
|
|
302
|
+
*
|
|
303
|
+
* Note: no extension pre-filter before the scan on purpose — GitHub
|
|
304
|
+
* Packages advertises tarball urls ending in a bare sha-256 digest.
|
|
305
|
+
* @param manifest cached manifest
|
|
306
|
+
* @param filename tarball file name, eg. pkg-1.0.0.tgz
|
|
307
|
+
*/
|
|
308
|
+
function lookupDistFile(manifest, filename) {
|
|
309
|
+
const distFile = manifest?._distfiles?.[filename];
|
|
310
|
+
if ((0, lodash_es.isNil)(distFile) === false) return distFile;
|
|
311
|
+
const versions = manifest?.versions ?? {};
|
|
312
|
+
const packageBasename = manifest?.name?.replace(/^.*\//, "");
|
|
313
|
+
if (packageBasename && filename.startsWith(`${packageBasename}-`) && filename.endsWith(".tgz")) {
|
|
314
|
+
const versionId = filename.slice(packageBasename.length + 1, -4);
|
|
315
|
+
const distFromVersion = distFileFromVersion(versions[versionId], filename);
|
|
316
|
+
if (distFromVersion) {
|
|
317
|
+
debug$1("distfile record missing for %o, using version %o dist", filename, versionId);
|
|
318
|
+
return distFromVersion;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
for (const versionId in versions) {
|
|
322
|
+
const distFromVersion = distFileFromVersion(versions[versionId], filename);
|
|
323
|
+
if (distFromVersion) {
|
|
324
|
+
debug$1("distfile record missing for %o, using version %o dist", filename, versionId);
|
|
325
|
+
return distFromVersion;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
debug$1("no distfile record or version dist found for %o", filename);
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Whether an uplink's configured url serves the given tarball url: same
|
|
333
|
+
* protocol, same host and the uplink path is a prefix of the tarball path.
|
|
334
|
+
* WHATWG URL normalizes default ports (https://h:443 === https://h).
|
|
335
|
+
*/
|
|
336
|
+
function uplinkServesUrl(uplinkUrl, tarballUrl) {
|
|
337
|
+
try {
|
|
338
|
+
const parsed = new URL(tarballUrl);
|
|
339
|
+
const basePath = uplinkUrl.pathname.endsWith("/") ? uplinkUrl.pathname : `${uplinkUrl.pathname}/`;
|
|
340
|
+
return parsed.protocol === uplinkUrl.protocol && parsed.host === uplinkUrl.host && parsed.pathname.startsWith(basePath);
|
|
341
|
+
} catch {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
272
345
|
//#endregion
|
|
273
346
|
exports.STORAGE = STORAGE;
|
|
274
347
|
exports.WHITELIST = WHITELIST;
|
|
275
348
|
exports.checkPackageRemote = checkPackageRemote;
|
|
276
349
|
exports.cleanUpLinksRef = cleanUpLinksRef;
|
|
277
350
|
exports.cleanUpReadme = cleanUpReadme;
|
|
351
|
+
exports.distFileFromVersion = distFileFromVersion;
|
|
278
352
|
exports.generatePackageTemplate = generatePackageTemplate;
|
|
279
353
|
exports.generateRevision = generateRevision;
|
|
280
354
|
exports.getLatestReadme = getLatestReadme;
|
|
281
355
|
exports.hasDeprecatedVersions = hasDeprecatedVersions;
|
|
282
356
|
exports.isDeprecatedManifest = isDeprecatedManifest;
|
|
357
|
+
exports.lookupDistFile = lookupDistFile;
|
|
283
358
|
exports.mapManifestToSearchPackageBody = mapManifestToSearchPackageBody;
|
|
284
359
|
exports.mergeUplinkTimeIntoLocal = mergeUplinkTimeIntoLocal;
|
|
285
360
|
exports.mergeUplinkTimeIntoLocalNext = mergeUplinkTimeIntoLocalNext;
|
|
@@ -288,6 +363,8 @@ exports.normalizeContributors = normalizeContributors;
|
|
|
288
363
|
exports.normalizeDistTags = normalizeDistTags;
|
|
289
364
|
exports.normalizePackage = normalizePackage;
|
|
290
365
|
exports.prepareSearchPackage = prepareSearchPackage;
|
|
366
|
+
exports.tarballMatchesFilename = tarballMatchesFilename;
|
|
291
367
|
exports.updateUpLinkMetadata = updateUpLinkMetadata;
|
|
368
|
+
exports.uplinkServesUrl = uplinkServesUrl;
|
|
292
369
|
|
|
293
370
|
//# sourceMappingURL=storage-utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-utils.js","names":[],"sources":["../../src/lib/storage-utils.ts"],"sourcesContent":["import { isNil, trim } from 'lodash-es';\nimport semver from 'semver';\n\nimport type { searchUtils } from '@verdaccio/core';\nimport {\n API_ERROR,\n DIST_TAGS,\n HTTP_STATUS,\n MAINTAINERS,\n USERS,\n cryptoUtils,\n errorUtils,\n pkgUtils,\n validationUtils,\n} from '@verdaccio/core';\nimport type { Author, GenericBody, Manifest, ReadmeOptions, Version } from '@verdaccio/types';\n\nimport { sortVersionsAndFilterInvalid } from './versions-utils';\n\nexport const STORAGE = {\n PACKAGE_FILE_NAME: 'package.json',\n FILE_EXIST_ERROR: 'EEXISTS',\n NO_SUCH_FILE_ERROR: 'ENOENT',\n DEFAULT_REVISION: '0-0000000000000000',\n};\n\n/**\n * Create a new package in the storage, return a boilerplate package\n * @param name package name\n * @returns {Manifest}\n */\nexport function generatePackageTemplate(name: string): Manifest {\n return {\n // standard things\n name,\n versions: {},\n time: {},\n [USERS]: {},\n [DIST_TAGS]: {},\n [MAINTAINERS]: [],\n _uplinks: {},\n _distfiles: {},\n _attachments: {},\n _rev: '',\n };\n}\n\n/**\n * Normalize package properties, tags, revision id.\n * @param {Object} pkg package reference.\n */\nexport function normalizePackage(pkg: Manifest): Manifest {\n const pkgProperties = ['versions', 'dist-tags', '_distfiles', '_attachments', '_uplinks', 'time'];\n\n pkgProperties.forEach((key): void => {\n const pkgProp = pkg[key];\n\n if (isNil(pkgProp) || validationUtils.isObject(pkgProp) === false) {\n pkg[key] = {};\n }\n });\n\n if (typeof pkg?._rev !== 'string') {\n pkg._rev = STORAGE.DEFAULT_REVISION;\n }\n\n if (typeof pkg?._id !== 'string') {\n pkg._id = pkg.name;\n }\n\n // normalize dist-tags\n return normalizeDistTags(pkg);\n}\n\nexport function generateRevision(rev: string): string {\n const _rev = rev.split('-');\n\n return (+_rev[0] || 0) + 1 + '-' + cryptoUtils.generateRandomHexString();\n}\n\nexport function getLatestReadme(pkg: Manifest): string {\n const versions = pkg['versions'] || {};\n const distTags = pkg[DIST_TAGS] || {};\n // FIXME: here is a bit tricky add the types\n const latestVersion: Version | any = distTags['latest'] ? versions[distTags['latest']] || {} : {};\n let readme = trim(pkg.readme || latestVersion.readme || '');\n if (readme) {\n return readme;\n }\n\n // In case of empty readme - trying to get ANY readme in the following order:\n // 'next','beta','alpha','test','dev','canary'\n const readmeDistTagsPriority = ['next', 'beta', 'alpha', 'test', 'dev', 'canary'];\n readmeDistTagsPriority.forEach(function (tag): string | void {\n if (readme) {\n return readme;\n }\n const version: Version | any = distTags[tag] ? versions[distTags[tag]] || {} : {};\n readme = trim(version.readme || readme);\n });\n return readme;\n}\n\n/**\n * Cleanup readme from package version\n *\n * By default, we don't keep readmes for package versions, only one readme per package.\n * Using publish.keep_readmes you can override this behavior and keep all readmes\n * or only readmes for tagged versions.\n */\nexport function cleanUpReadme(\n version: Version,\n distTags?: GenericBody,\n keepReadmes?: ReadmeOptions\n): Version {\n if (keepReadmes === 'all') {\n return version;\n } else if (keepReadmes === 'tagged') {\n if (distTags && Object.values(distTags).includes(version.version)) {\n return version;\n }\n }\n\n if (isNil(version) === false) {\n version.readme = '';\n }\n\n return version;\n}\n\nexport const WHITELIST = [\n '_rev',\n 'name',\n 'versions',\n 'dist-tags',\n 'readme',\n 'time',\n '_id',\n 'users',\n 'maintainers',\n];\n\nexport function cleanUpLinksRef(result: Manifest, keepUpLinkData?: boolean): Manifest {\n const propertyToKeep = [...WHITELIST];\n if (keepUpLinkData === true) {\n propertyToKeep.push('_uplinks');\n }\n\n for (const i in result) {\n if (propertyToKeep.indexOf(i) === -1) {\n // Remove sections like '_uplinks' from response\n delete result[i];\n }\n }\n\n return result;\n}\n\n// TODO: move to abstract storage\n// @deprecated use abstract.storage.ts:checkPackageRemote\nexport function checkPackageRemote(\n name: string,\n isAllowPublishOffline: boolean,\n syncMetadata: Function\n): Promise<any> {\n return new Promise<void>((resolve, reject): void => {\n syncMetadata(name, null, {}, (err, packageJsonLocal, upLinksErrors): void => {\n // something weird\n if (err && err.status !== HTTP_STATUS.NOT_FOUND) {\n return reject(err);\n }\n\n // checking package exist already\n if (isNil(packageJsonLocal) === false) {\n return reject(errorUtils.getConflict(API_ERROR.PACKAGE_EXIST));\n }\n\n for (let errorItem = 0; errorItem < upLinksErrors.length; errorItem++) {\n // checking error\n // if uplink fails with a status other than 404, we report failure\n if (isNil(upLinksErrors[errorItem][0]) === false) {\n if (upLinksErrors[errorItem][0].status !== HTTP_STATUS.NOT_FOUND) {\n if (isAllowPublishOffline) {\n return resolve();\n }\n\n return reject(errorUtils.getServiceUnavailable(API_ERROR.UPLINK_OFFLINE_PUBLISH));\n }\n }\n }\n\n return resolve();\n });\n });\n}\n\n// @deprecated use export function mergeUplinkTimeIntoLocalNext\nexport function mergeUplinkTimeIntoLocal(localMetadata: Manifest, remoteMetadata: Manifest): any {\n if ('time' in remoteMetadata) {\n return Object.assign({}, localMetadata.time, remoteMetadata.time);\n }\n\n return localMetadata.time;\n}\n\nexport function mergeUplinkTimeIntoLocalNext(\n cacheManifest: Manifest,\n remoteManifest: Manifest\n): Manifest {\n if ('time' in remoteManifest) {\n // remote override cache time conflicts\n return { ...cacheManifest, time: { ...cacheManifest.time, ...remoteManifest.time } };\n }\n\n return cacheManifest;\n}\n\nexport function updateUpLinkMetadata(uplinkName: string, manifest: Manifest, etag: string) {\n const _uplinks = {\n ...manifest._uplinks,\n [uplinkName]: {\n etag,\n fetched: Date.now(),\n },\n };\n return {\n ...manifest,\n _uplinks,\n };\n}\n\nexport function prepareSearchPackage(data: Manifest): any {\n const latest = pkgUtils.getLatest(data);\n\n if (latest && data.versions[latest]) {\n const version: Version = data.versions[latest];\n const versions: any = { [latest]: 'latest' };\n const pkg: any = {\n name: version.name,\n description: version.description,\n [DIST_TAGS]: { latest },\n maintainers: version.maintainers || [version.author].filter(Boolean),\n author: version.author,\n repository: version.repository,\n readmeFilename: version.readmeFilename || '',\n homepage: version.homepage,\n keywords: version.keywords,\n bugs: version.bugs,\n license: version.license,\n // time: {\n // modified: time,\n // },\n versions,\n };\n\n return pkg;\n }\n}\n\n/**\n * Function gets a local info and an info from uplinks and tries to merge it\n exported for unit tests only.\n * @param {*} cacheManifest\n * @param {*} remoteManifest\n */\nexport function mergeVersions(cacheManifest: Manifest, remoteManifest: Manifest): Manifest {\n let _cacheManifest = { ...cacheManifest };\n const { versions, time } = remoteManifest;\n // copy new versions to a cache\n // NOTE: if a certain version was updated, we can't refresh it reliably\n // however, we can refresh the deprecated status\n for (const i in versions) {\n if (typeof _cacheManifest.versions[i] === 'undefined') {\n _cacheManifest.versions[i] = versions[i];\n } else if (versions[i].deprecated) {\n _cacheManifest.versions[i].deprecated = versions[i].deprecated;\n }\n }\n\n for (const i in time) {\n if (typeof _cacheManifest.time[i] === 'undefined') {\n _cacheManifest.time[i] = time[i];\n }\n }\n\n for (const distTag in remoteManifest[DIST_TAGS]) {\n if (_cacheManifest[DIST_TAGS][distTag] !== remoteManifest[DIST_TAGS][distTag]) {\n if (\n !_cacheManifest[DIST_TAGS][distTag] ||\n semver.lte(_cacheManifest[DIST_TAGS][distTag], remoteManifest[DIST_TAGS][distTag])\n ) {\n _cacheManifest[DIST_TAGS][distTag] = remoteManifest[DIST_TAGS][distTag];\n }\n if (\n distTag === 'latest' &&\n _cacheManifest[DIST_TAGS][distTag] === remoteManifest[DIST_TAGS][distTag]\n ) {\n // NOTE: this override the latest publish readme from local cache with\n // the remote one\n _cacheManifest = { ..._cacheManifest, readme: remoteManifest.readme };\n }\n }\n }\n return _cacheManifest;\n}\n\n/**\n * Normalize dist-tags.\n *\n * There is a legacy behaviour where the dist-tags could be an array, in such\n * case, the array is orderded and the highest version becames the\n * latest.\n *\n * The dist-tag tags must be plain strigs, if the latest is empty (for whatever reason) is\n * normalized to be the highest version available.\n *\n * This function cleans up every invalid version on dist-tags, but does not remove\n * invalid versions from the manifest.\n *\n * @param {*} data\n */\nexport function normalizeDistTags(manifest: Manifest): Manifest {\n let sorted;\n // handle missing latest dist-tag\n if (!manifest[DIST_TAGS].latest) {\n // if there is no latest tag, set the highest known version based on semver sort\n sorted = sortVersionsAndFilterInvalid(Object.keys(manifest.versions));\n if (sorted?.length) {\n // get the highest published version\n manifest[DIST_TAGS].latest = sorted.pop();\n }\n }\n\n for (const tag in manifest[DIST_TAGS]) {\n // deprecated (will be removed un future majors)\n // this should not happen, tags should be plain strings, legacy fallback\n if (Array.isArray(manifest[DIST_TAGS][tag])) {\n if (manifest[DIST_TAGS][tag].length) {\n // sort array\n // FIXME: this is clearly wrong, we need to research why this is like this.\n // @ts-ignore\n sorted = sortVersionsAndFilterInvalid(manifest[DIST_TAGS][tag]);\n if (sorted.length) {\n // use highest version based on semver sort\n manifest[DIST_TAGS][tag] = sorted.pop();\n }\n } else {\n delete manifest[DIST_TAGS][tag];\n }\n } else if (typeof manifest[DIST_TAGS][tag] === 'string') {\n if (!semver.parse(manifest[DIST_TAGS][tag], true)) {\n // if the version is invalid, delete the dist-tag entry\n delete manifest[DIST_TAGS][tag];\n }\n }\n }\n\n return manifest;\n}\n\nexport function hasDeprecatedVersions(pkgInfo: Manifest): boolean {\n const { versions } = pkgInfo;\n for (const version in versions) {\n if (Object.prototype.hasOwnProperty.call(versions[version], 'deprecated')) {\n return true;\n }\n }\n return false;\n}\n\nexport function isDeprecatedManifest(manifest: Manifest): boolean {\n return (\n hasDeprecatedVersions(manifest) &&\n (typeof manifest._attachments === 'undefined' ||\n Object.keys(manifest._attachments).length === 0)\n );\n}\n\nexport function mapManifestToSearchPackageBody(\n pkg: Manifest,\n searchItem: searchUtils.SearchItem\n): searchUtils.SearchPackageBody {\n const latest = pkgUtils.getLatest(pkg);\n const version: Version = pkg.versions[latest];\n // packument maintainers carry `name`, but the npm search API contract uses\n // `username` and the npm CLI crashes on entries without it — emit both\n const maintainers = Array.isArray(version.maintainers)\n ? version.maintainers.map((maintainer: any) =>\n typeof maintainer === 'string'\n ? { username: maintainer, email: '' }\n : {\n ...maintainer,\n username: maintainer?.username ?? maintainer?.name ?? '',\n email: maintainer?.email ?? '',\n }\n )\n : [];\n // npmjs fills `publisher` with the user who published the version; use\n // `_npmUser` when the client provided it and fall back to the first\n // maintainer (verdaccio sets it to the publishing user), otherwise the\n // npm CLI renders \"published by ???\"\n const publisher = version._npmUser?.name\n ? { username: version._npmUser.name, email: version._npmUser.email ?? '' }\n : (maintainers[0] ?? {});\n const result: searchUtils.SearchPackageBody = {\n name: version.name,\n scope: '',\n description: version.description,\n version: latest,\n keywords: version.keywords,\n date: pkg.time[latest],\n // FIXME: type\n author: version.author as any,\n publisher,\n maintainers,\n links: {\n npm: '',\n homepage: version.homepage,\n repository: version.repository,\n bugs: version.bugs,\n },\n };\n\n if (typeof searchItem.package.scoped === 'string') {\n result.scope = searchItem.package.scoped;\n }\n\n return result;\n}\n\nexport function normalizeContributors(contributors: Author[]): Author[] {\n if (isNil(contributors)) {\n return [];\n } else if (contributors && !Array.isArray(contributors)) {\n return [contributors];\n } else if (typeof contributors === 'string') {\n return [\n {\n name: contributors,\n },\n ];\n }\n\n return contributors;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"storage-utils.js","names":[],"sources":["../../src/lib/storage-utils.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { isNil, trim } from 'lodash-es';\nimport semver from 'semver';\n\nimport type { searchUtils } from '@verdaccio/core';\nimport {\n API_ERROR,\n DIST_TAGS,\n HTTP_STATUS,\n MAINTAINERS,\n USERS,\n cryptoUtils,\n errorUtils,\n pkgUtils,\n validationUtils,\n} from '@verdaccio/core';\nimport type {\n Author,\n DistFile,\n GenericBody,\n Manifest,\n ReadmeOptions,\n Version,\n} from '@verdaccio/types';\n\nimport { sortVersionsAndFilterInvalid } from './versions-utils';\n\nconst debug = buildDebug('verdaccio:storage:utils');\n\nexport const STORAGE = {\n PACKAGE_FILE_NAME: 'package.json',\n FILE_EXIST_ERROR: 'EEXISTS',\n NO_SUCH_FILE_ERROR: 'ENOENT',\n DEFAULT_REVISION: '0-0000000000000000',\n};\n\n/**\n * Create a new package in the storage, return a boilerplate package\n * @param name package name\n * @returns {Manifest}\n */\nexport function generatePackageTemplate(name: string): Manifest {\n return {\n // standard things\n name,\n versions: {},\n time: {},\n [USERS]: {},\n [DIST_TAGS]: {},\n [MAINTAINERS]: [],\n _uplinks: {},\n _distfiles: {},\n _attachments: {},\n _rev: '',\n };\n}\n\n/**\n * Normalize package properties, tags, revision id.\n * @param {Object} pkg package reference.\n */\nexport function normalizePackage(pkg: Manifest): Manifest {\n const pkgProperties = ['versions', 'dist-tags', '_distfiles', '_attachments', '_uplinks', 'time'];\n\n pkgProperties.forEach((key): void => {\n const pkgProp = pkg[key];\n\n if (isNil(pkgProp) || validationUtils.isObject(pkgProp) === false) {\n pkg[key] = {};\n }\n });\n\n if (typeof pkg?._rev !== 'string') {\n pkg._rev = STORAGE.DEFAULT_REVISION;\n }\n\n if (typeof pkg?._id !== 'string') {\n pkg._id = pkg.name;\n }\n\n // normalize dist-tags\n return normalizeDistTags(pkg);\n}\n\nexport function generateRevision(rev: string): string {\n const _rev = rev.split('-');\n\n return (+_rev[0] || 0) + 1 + '-' + cryptoUtils.generateRandomHexString();\n}\n\nexport function getLatestReadme(pkg: Manifest): string {\n const versions = pkg['versions'] || {};\n const distTags = pkg[DIST_TAGS] || {};\n // FIXME: here is a bit tricky add the types\n const latestVersion: Version | any = distTags['latest'] ? versions[distTags['latest']] || {} : {};\n let readme = trim(pkg.readme || latestVersion.readme || '');\n if (readme) {\n return readme;\n }\n\n // In case of empty readme - trying to get ANY readme in the following order:\n // 'next','beta','alpha','test','dev','canary'\n const readmeDistTagsPriority = ['next', 'beta', 'alpha', 'test', 'dev', 'canary'];\n readmeDistTagsPriority.forEach(function (tag): string | void {\n if (readme) {\n return readme;\n }\n const version: Version | any = distTags[tag] ? versions[distTags[tag]] || {} : {};\n readme = trim(version.readme || readme);\n });\n return readme;\n}\n\n/**\n * Cleanup readme from package version\n *\n * By default, we don't keep readmes for package versions, only one readme per package.\n * Using publish.keep_readmes you can override this behavior and keep all readmes\n * or only readmes for tagged versions.\n */\nexport function cleanUpReadme(\n version: Version,\n distTags?: GenericBody,\n keepReadmes?: ReadmeOptions\n): Version {\n if (keepReadmes === 'all') {\n return version;\n } else if (keepReadmes === 'tagged') {\n if (distTags && Object.values(distTags).includes(version.version)) {\n return version;\n }\n }\n\n if (isNil(version) === false) {\n version.readme = '';\n }\n\n return version;\n}\n\nexport const WHITELIST = [\n '_rev',\n 'name',\n 'versions',\n 'dist-tags',\n 'readme',\n 'time',\n '_id',\n 'users',\n 'maintainers',\n];\n\nexport function cleanUpLinksRef(result: Manifest, keepUpLinkData?: boolean): Manifest {\n const propertyToKeep = [...WHITELIST];\n if (keepUpLinkData === true) {\n propertyToKeep.push('_uplinks');\n }\n\n for (const i in result) {\n if (propertyToKeep.indexOf(i) === -1) {\n // Remove sections like '_uplinks' from response\n delete result[i];\n }\n }\n\n return result;\n}\n\n// TODO: move to abstract storage\n// @deprecated use abstract.storage.ts:checkPackageRemote\nexport function checkPackageRemote(\n name: string,\n isAllowPublishOffline: boolean,\n syncMetadata: Function\n): Promise<any> {\n return new Promise<void>((resolve, reject): void => {\n syncMetadata(name, null, {}, (err, packageJsonLocal, upLinksErrors): void => {\n // something weird\n if (err && err.status !== HTTP_STATUS.NOT_FOUND) {\n return reject(err);\n }\n\n // checking package exist already\n if (isNil(packageJsonLocal) === false) {\n return reject(errorUtils.getConflict(API_ERROR.PACKAGE_EXIST));\n }\n\n for (let errorItem = 0; errorItem < upLinksErrors.length; errorItem++) {\n // checking error\n // if uplink fails with a status other than 404, we report failure\n if (isNil(upLinksErrors[errorItem][0]) === false) {\n if (upLinksErrors[errorItem][0].status !== HTTP_STATUS.NOT_FOUND) {\n if (isAllowPublishOffline) {\n return resolve();\n }\n\n return reject(errorUtils.getServiceUnavailable(API_ERROR.UPLINK_OFFLINE_PUBLISH));\n }\n }\n }\n\n return resolve();\n });\n });\n}\n\n// @deprecated use export function mergeUplinkTimeIntoLocalNext\nexport function mergeUplinkTimeIntoLocal(localMetadata: Manifest, remoteMetadata: Manifest): any {\n if ('time' in remoteMetadata) {\n return Object.assign({}, localMetadata.time, remoteMetadata.time);\n }\n\n return localMetadata.time;\n}\n\nexport function mergeUplinkTimeIntoLocalNext(\n cacheManifest: Manifest,\n remoteManifest: Manifest\n): Manifest {\n if ('time' in remoteManifest) {\n // remote override cache time conflicts\n return { ...cacheManifest, time: { ...cacheManifest.time, ...remoteManifest.time } };\n }\n\n return cacheManifest;\n}\n\nexport function updateUpLinkMetadata(uplinkName: string, manifest: Manifest, etag: string) {\n const _uplinks = {\n ...manifest._uplinks,\n [uplinkName]: {\n etag,\n fetched: Date.now(),\n },\n };\n return {\n ...manifest,\n _uplinks,\n };\n}\n\nexport function prepareSearchPackage(data: Manifest): any {\n const latest = pkgUtils.getLatest(data);\n\n if (latest && data.versions[latest]) {\n const version: Version = data.versions[latest];\n const versions: any = { [latest]: 'latest' };\n const pkg: any = {\n name: version.name,\n description: version.description,\n [DIST_TAGS]: { latest },\n maintainers: version.maintainers || [version.author].filter(Boolean),\n author: version.author,\n repository: version.repository,\n readmeFilename: version.readmeFilename || '',\n homepage: version.homepage,\n keywords: version.keywords,\n bugs: version.bugs,\n license: version.license,\n // time: {\n // modified: time,\n // },\n versions,\n };\n\n return pkg;\n }\n}\n\n/**\n * Function gets a local info and an info from uplinks and tries to merge it\n exported for unit tests only.\n * @param {*} cacheManifest\n * @param {*} remoteManifest\n */\nexport function mergeVersions(cacheManifest: Manifest, remoteManifest: Manifest): Manifest {\n let _cacheManifest = { ...cacheManifest };\n const { versions, time } = remoteManifest;\n // copy new versions to a cache\n // NOTE: if a certain version was updated, we can't refresh it reliably\n // however, we can refresh the deprecated status\n for (const i in versions) {\n if (typeof _cacheManifest.versions[i] === 'undefined') {\n _cacheManifest.versions[i] = versions[i];\n } else if (versions[i].deprecated) {\n _cacheManifest.versions[i].deprecated = versions[i].deprecated;\n }\n }\n\n for (const i in time) {\n if (typeof _cacheManifest.time[i] === 'undefined') {\n _cacheManifest.time[i] = time[i];\n }\n }\n\n for (const distTag in remoteManifest[DIST_TAGS]) {\n if (_cacheManifest[DIST_TAGS][distTag] !== remoteManifest[DIST_TAGS][distTag]) {\n if (\n !_cacheManifest[DIST_TAGS][distTag] ||\n semver.lte(_cacheManifest[DIST_TAGS][distTag], remoteManifest[DIST_TAGS][distTag])\n ) {\n _cacheManifest[DIST_TAGS][distTag] = remoteManifest[DIST_TAGS][distTag];\n }\n if (\n distTag === 'latest' &&\n _cacheManifest[DIST_TAGS][distTag] === remoteManifest[DIST_TAGS][distTag]\n ) {\n // NOTE: this override the latest publish readme from local cache with\n // the remote one\n _cacheManifest = { ..._cacheManifest, readme: remoteManifest.readme };\n }\n }\n }\n return _cacheManifest;\n}\n\n/**\n * Normalize dist-tags.\n *\n * There is a legacy behaviour where the dist-tags could be an array, in such\n * case, the array is orderded and the highest version becames the\n * latest.\n *\n * The dist-tag tags must be plain strigs, if the latest is empty (for whatever reason) is\n * normalized to be the highest version available.\n *\n * This function cleans up every invalid version on dist-tags, but does not remove\n * invalid versions from the manifest.\n *\n * @param {*} data\n */\nexport function normalizeDistTags(manifest: Manifest): Manifest {\n let sorted;\n // handle missing latest dist-tag\n if (!manifest[DIST_TAGS].latest) {\n // if there is no latest tag, set the highest known version based on semver sort\n sorted = sortVersionsAndFilterInvalid(Object.keys(manifest.versions));\n if (sorted?.length) {\n // get the highest published version\n manifest[DIST_TAGS].latest = sorted.pop();\n }\n }\n\n for (const tag in manifest[DIST_TAGS]) {\n // deprecated (will be removed un future majors)\n // this should not happen, tags should be plain strings, legacy fallback\n if (Array.isArray(manifest[DIST_TAGS][tag])) {\n if (manifest[DIST_TAGS][tag].length) {\n // sort array\n // FIXME: this is clearly wrong, we need to research why this is like this.\n // @ts-ignore\n sorted = sortVersionsAndFilterInvalid(manifest[DIST_TAGS][tag]);\n if (sorted.length) {\n // use highest version based on semver sort\n manifest[DIST_TAGS][tag] = sorted.pop();\n }\n } else {\n delete manifest[DIST_TAGS][tag];\n }\n } else if (typeof manifest[DIST_TAGS][tag] === 'string') {\n if (!semver.parse(manifest[DIST_TAGS][tag], true)) {\n // if the version is invalid, delete the dist-tag entry\n delete manifest[DIST_TAGS][tag];\n }\n }\n }\n\n return manifest;\n}\n\nexport function hasDeprecatedVersions(pkgInfo: Manifest): boolean {\n const { versions } = pkgInfo;\n for (const version in versions) {\n if (Object.prototype.hasOwnProperty.call(versions[version], 'deprecated')) {\n return true;\n }\n }\n return false;\n}\n\nexport function isDeprecatedManifest(manifest: Manifest): boolean {\n return (\n hasDeprecatedVersions(manifest) &&\n (typeof manifest._attachments === 'undefined' ||\n Object.keys(manifest._attachments).length === 0)\n );\n}\n\nexport function mapManifestToSearchPackageBody(\n pkg: Manifest,\n _searchItem: searchUtils.SearchItem\n): searchUtils.SearchPackageBody {\n const latest = pkgUtils.getLatest(pkg);\n const version: Version = pkg.versions[latest];\n // packument maintainers carry `name`, but the npm search API contract uses\n // `username` and the npm CLI crashes on entries without it — emit both\n const maintainers = Array.isArray(version.maintainers)\n ? version.maintainers.map((maintainer: any) =>\n typeof maintainer === 'string'\n ? { username: maintainer, email: '' }\n : {\n ...maintainer,\n username: maintainer?.username ?? maintainer?.name ?? '',\n email: maintainer?.email ?? '',\n }\n )\n : [];\n // npmjs fills `publisher` with the user who published the version; use\n // `_npmUser` when the client provided it and fall back to the first\n // maintainer (verdaccio sets it to the publishing user), otherwise the\n // npm CLI renders \"published by ???\"\n const publisher = version._npmUser?.name\n ? { username: version._npmUser.name, email: version._npmUser.email ?? '' }\n : (maintainers[0] ?? {});\n const links: NonNullable<searchUtils.SearchPackageBody['links']> = {};\n const repository =\n typeof version.repository === 'string' ? version.repository : version.repository?.url;\n const bugs = typeof version.bugs === 'string' ? version.bugs : version.bugs?.url;\n if (version.homepage) {\n links.homepage = version.homepage;\n }\n if (repository) {\n links.repository = repository;\n }\n if (bugs) {\n links.bugs = bugs;\n }\n const result: searchUtils.SearchPackageBody = {\n name: version.name,\n description: version.description,\n version: latest,\n license: version.license,\n keywords: version.keywords,\n date: pkg.time[latest],\n // FIXME: type\n author: version.author as any,\n publisher,\n maintainers,\n };\n\n if (Object.keys(links).length > 0) {\n result.links = links;\n }\n\n return result;\n}\n\nexport function normalizeContributors(contributors: Author[]): Author[] {\n if (isNil(contributors)) {\n return [];\n } else if (contributors && !Array.isArray(contributors)) {\n return [contributors];\n } else if (typeof contributors === 'string') {\n return [\n {\n name: contributors,\n },\n ];\n }\n\n return contributors;\n}\n\n/**\n * Whether the tarball url path ends with the given file name (any query\n * string or fragment is ignored, like the path parsing in\n * `tarballUtils.extractTarballFromUrl`, which creates the `_distfiles`\n * keys — if that parsing ever changes, this must change with it).\n */\nexport function tarballMatchesFilename(tarball: string, filename: string): boolean {\n return tarball.split(/[?#]/)[0].endsWith(`/${filename}`);\n}\n\n/**\n * Build a distfile record from a version's dist metadata when its tarball\n * url matches the given file name.\n */\nexport function distFileFromVersion(\n version: Version | undefined,\n filename: string\n): DistFile | null {\n const dist = version?.dist;\n\n if (dist?.tarball && tarballMatchesFilename(dist.tarball, filename)) {\n return { url: dist.tarball, sha: dist.shasum } as DistFile;\n }\n\n return null;\n}\n\n/**\n * Resolve the distfile record for a tarball filename. Prefers the\n * `_distfiles` bookkeeping, but falls back to the version metadata when the\n * record is missing (storages written by other verdaccio versions cache\n * versions without their distfile records, which made those tarballs\n * permanently unavailable).\n *\n * Note: no extension pre-filter before the scan on purpose — GitHub\n * Packages advertises tarball urls ending in a bare sha-256 digest.\n * @param manifest cached manifest\n * @param filename tarball file name, eg. pkg-1.0.0.tgz\n */\nexport function lookupDistFile(manifest: Manifest, filename: string): DistFile | null {\n const distFile = manifest?._distfiles?.[filename];\n if (isNil(distFile) === false) {\n return distFile;\n }\n\n const versions = manifest?.versions ?? {};\n\n // tarballs are conventionally named <basename>-<version>.tgz, so the\n // version can usually be derived from the filename without scanning\n // manifests that carry thousands of versions\n const packageBasename = manifest?.name?.replace(/^.*\\//, '');\n if (packageBasename && filename.startsWith(`${packageBasename}-`) && filename.endsWith('.tgz')) {\n const versionId = filename.slice(packageBasename.length + 1, -'.tgz'.length);\n const distFromVersion = distFileFromVersion(versions[versionId], filename);\n\n if (distFromVersion) {\n debug('distfile record missing for %o, using version %o dist', filename, versionId);\n return distFromVersion;\n }\n }\n\n // unconventional tarball names: fall back to a scan\n for (const versionId in versions) {\n const distFromVersion = distFileFromVersion(versions[versionId], filename);\n\n if (distFromVersion) {\n debug('distfile record missing for %o, using version %o dist', filename, versionId);\n return distFromVersion;\n }\n }\n\n debug('no distfile record or version dist found for %o', filename);\n return null;\n}\n\n/**\n * Whether an uplink's configured url serves the given tarball url: same\n * protocol, same host and the uplink path is a prefix of the tarball path.\n * WHATWG URL normalizes default ports (https://h:443 === https://h).\n */\nexport function uplinkServesUrl(uplinkUrl: URL, tarballUrl: string): boolean {\n try {\n const parsed = new URL(tarballUrl);\n // Compare on a path segment boundary: an uplink at /registry must not\n // match tarballs under /registry2/... (wrong-uplink selection would send\n // the uplink's credentials to a sibling path).\n const basePath = uplinkUrl.pathname.endsWith('/')\n ? uplinkUrl.pathname\n : `${uplinkUrl.pathname}/`;\n return (\n parsed.protocol === uplinkUrl.protocol &&\n parsed.host === uplinkUrl.host &&\n parsed.pathname.startsWith(basePath)\n );\n } catch {\n return false;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { sortVersionsAndFilterInvalid } from "./versions-utils.mjs";
|
|
2
|
+
import buildDebug from "debug";
|
|
2
3
|
import { isNil, trim } from "lodash-es";
|
|
3
4
|
import { API_ERROR, DIST_TAGS, HTTP_STATUS, MAINTAINERS, USERS, cryptoUtils, errorUtils, pkgUtils, validationUtils } from "@verdaccio/core";
|
|
4
5
|
import semver from "semver";
|
|
5
6
|
//#region src/lib/storage-utils.ts
|
|
7
|
+
var debug = buildDebug("verdaccio:storage:utils");
|
|
6
8
|
var STORAGE = {
|
|
7
9
|
PACKAGE_FILE_NAME: "package.json",
|
|
8
10
|
FILE_EXIST_ERROR: "EEXISTS",
|
|
@@ -226,7 +228,7 @@ function hasDeprecatedVersions(pkgInfo) {
|
|
|
226
228
|
function isDeprecatedManifest(manifest) {
|
|
227
229
|
return hasDeprecatedVersions(manifest) && (typeof manifest._attachments === "undefined" || Object.keys(manifest._attachments).length === 0);
|
|
228
230
|
}
|
|
229
|
-
function mapManifestToSearchPackageBody(pkg,
|
|
231
|
+
function mapManifestToSearchPackageBody(pkg, _searchItem) {
|
|
230
232
|
const latest = pkgUtils.getLatest(pkg);
|
|
231
233
|
const version = pkg.versions[latest];
|
|
232
234
|
const maintainers = Array.isArray(version.maintainers) ? version.maintainers.map((maintainer) => typeof maintainer === "string" ? {
|
|
@@ -241,24 +243,24 @@ function mapManifestToSearchPackageBody(pkg, searchItem) {
|
|
|
241
243
|
username: version._npmUser.name,
|
|
242
244
|
email: version._npmUser.email ?? ""
|
|
243
245
|
} : maintainers[0] ?? {};
|
|
246
|
+
const links = {};
|
|
247
|
+
const repository = typeof version.repository === "string" ? version.repository : version.repository?.url;
|
|
248
|
+
const bugs = typeof version.bugs === "string" ? version.bugs : version.bugs?.url;
|
|
249
|
+
if (version.homepage) links.homepage = version.homepage;
|
|
250
|
+
if (repository) links.repository = repository;
|
|
251
|
+
if (bugs) links.bugs = bugs;
|
|
244
252
|
const result = {
|
|
245
253
|
name: version.name,
|
|
246
|
-
scope: "",
|
|
247
254
|
description: version.description,
|
|
248
255
|
version: latest,
|
|
256
|
+
license: version.license,
|
|
249
257
|
keywords: version.keywords,
|
|
250
258
|
date: pkg.time[latest],
|
|
251
259
|
author: version.author,
|
|
252
260
|
publisher,
|
|
253
|
-
maintainers
|
|
254
|
-
links: {
|
|
255
|
-
npm: "",
|
|
256
|
-
homepage: version.homepage,
|
|
257
|
-
repository: version.repository,
|
|
258
|
-
bugs: version.bugs
|
|
259
|
-
}
|
|
261
|
+
maintainers
|
|
260
262
|
};
|
|
261
|
-
if (
|
|
263
|
+
if (Object.keys(links).length > 0) result.links = links;
|
|
262
264
|
return result;
|
|
263
265
|
}
|
|
264
266
|
function normalizeContributors(contributors) {
|
|
@@ -267,7 +269,77 @@ function normalizeContributors(contributors) {
|
|
|
267
269
|
else if (typeof contributors === "string") return [{ name: contributors }];
|
|
268
270
|
return contributors;
|
|
269
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Whether the tarball url path ends with the given file name (any query
|
|
274
|
+
* string or fragment is ignored, like the path parsing in
|
|
275
|
+
* `tarballUtils.extractTarballFromUrl`, which creates the `_distfiles`
|
|
276
|
+
* keys — if that parsing ever changes, this must change with it).
|
|
277
|
+
*/
|
|
278
|
+
function tarballMatchesFilename(tarball, filename) {
|
|
279
|
+
return tarball.split(/[?#]/)[0].endsWith(`/${filename}`);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Build a distfile record from a version's dist metadata when its tarball
|
|
283
|
+
* url matches the given file name.
|
|
284
|
+
*/
|
|
285
|
+
function distFileFromVersion(version, filename) {
|
|
286
|
+
const dist = version?.dist;
|
|
287
|
+
if (dist?.tarball && tarballMatchesFilename(dist.tarball, filename)) return {
|
|
288
|
+
url: dist.tarball,
|
|
289
|
+
sha: dist.shasum
|
|
290
|
+
};
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Resolve the distfile record for a tarball filename. Prefers the
|
|
295
|
+
* `_distfiles` bookkeeping, but falls back to the version metadata when the
|
|
296
|
+
* record is missing (storages written by other verdaccio versions cache
|
|
297
|
+
* versions without their distfile records, which made those tarballs
|
|
298
|
+
* permanently unavailable).
|
|
299
|
+
*
|
|
300
|
+
* Note: no extension pre-filter before the scan on purpose — GitHub
|
|
301
|
+
* Packages advertises tarball urls ending in a bare sha-256 digest.
|
|
302
|
+
* @param manifest cached manifest
|
|
303
|
+
* @param filename tarball file name, eg. pkg-1.0.0.tgz
|
|
304
|
+
*/
|
|
305
|
+
function lookupDistFile(manifest, filename) {
|
|
306
|
+
const distFile = manifest?._distfiles?.[filename];
|
|
307
|
+
if (isNil(distFile) === false) return distFile;
|
|
308
|
+
const versions = manifest?.versions ?? {};
|
|
309
|
+
const packageBasename = manifest?.name?.replace(/^.*\//, "");
|
|
310
|
+
if (packageBasename && filename.startsWith(`${packageBasename}-`) && filename.endsWith(".tgz")) {
|
|
311
|
+
const versionId = filename.slice(packageBasename.length + 1, -4);
|
|
312
|
+
const distFromVersion = distFileFromVersion(versions[versionId], filename);
|
|
313
|
+
if (distFromVersion) {
|
|
314
|
+
debug("distfile record missing for %o, using version %o dist", filename, versionId);
|
|
315
|
+
return distFromVersion;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
for (const versionId in versions) {
|
|
319
|
+
const distFromVersion = distFileFromVersion(versions[versionId], filename);
|
|
320
|
+
if (distFromVersion) {
|
|
321
|
+
debug("distfile record missing for %o, using version %o dist", filename, versionId);
|
|
322
|
+
return distFromVersion;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
debug("no distfile record or version dist found for %o", filename);
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Whether an uplink's configured url serves the given tarball url: same
|
|
330
|
+
* protocol, same host and the uplink path is a prefix of the tarball path.
|
|
331
|
+
* WHATWG URL normalizes default ports (https://h:443 === https://h).
|
|
332
|
+
*/
|
|
333
|
+
function uplinkServesUrl(uplinkUrl, tarballUrl) {
|
|
334
|
+
try {
|
|
335
|
+
const parsed = new URL(tarballUrl);
|
|
336
|
+
const basePath = uplinkUrl.pathname.endsWith("/") ? uplinkUrl.pathname : `${uplinkUrl.pathname}/`;
|
|
337
|
+
return parsed.protocol === uplinkUrl.protocol && parsed.host === uplinkUrl.host && parsed.pathname.startsWith(basePath);
|
|
338
|
+
} catch {
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
270
342
|
//#endregion
|
|
271
|
-
export { STORAGE, WHITELIST, checkPackageRemote, cleanUpLinksRef, cleanUpReadme, generatePackageTemplate, generateRevision, getLatestReadme, hasDeprecatedVersions, isDeprecatedManifest, mapManifestToSearchPackageBody, mergeUplinkTimeIntoLocal, mergeUplinkTimeIntoLocalNext, mergeVersions, normalizeContributors, normalizeDistTags, normalizePackage, prepareSearchPackage, updateUpLinkMetadata };
|
|
343
|
+
export { STORAGE, WHITELIST, checkPackageRemote, cleanUpLinksRef, cleanUpReadme, distFileFromVersion, generatePackageTemplate, generateRevision, getLatestReadme, hasDeprecatedVersions, isDeprecatedManifest, lookupDistFile, mapManifestToSearchPackageBody, mergeUplinkTimeIntoLocal, mergeUplinkTimeIntoLocalNext, mergeVersions, normalizeContributors, normalizeDistTags, normalizePackage, prepareSearchPackage, tarballMatchesFilename, updateUpLinkMetadata, uplinkServesUrl };
|
|
272
344
|
|
|
273
345
|
//# sourceMappingURL=storage-utils.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-utils.mjs","names":[],"sources":["../../src/lib/storage-utils.ts"],"sourcesContent":["import { isNil, trim } from 'lodash-es';\nimport semver from 'semver';\n\nimport type { searchUtils } from '@verdaccio/core';\nimport {\n API_ERROR,\n DIST_TAGS,\n HTTP_STATUS,\n MAINTAINERS,\n USERS,\n cryptoUtils,\n errorUtils,\n pkgUtils,\n validationUtils,\n} from '@verdaccio/core';\nimport type { Author, GenericBody, Manifest, ReadmeOptions, Version } from '@verdaccio/types';\n\nimport { sortVersionsAndFilterInvalid } from './versions-utils';\n\nexport const STORAGE = {\n PACKAGE_FILE_NAME: 'package.json',\n FILE_EXIST_ERROR: 'EEXISTS',\n NO_SUCH_FILE_ERROR: 'ENOENT',\n DEFAULT_REVISION: '0-0000000000000000',\n};\n\n/**\n * Create a new package in the storage, return a boilerplate package\n * @param name package name\n * @returns {Manifest}\n */\nexport function generatePackageTemplate(name: string): Manifest {\n return {\n // standard things\n name,\n versions: {},\n time: {},\n [USERS]: {},\n [DIST_TAGS]: {},\n [MAINTAINERS]: [],\n _uplinks: {},\n _distfiles: {},\n _attachments: {},\n _rev: '',\n };\n}\n\n/**\n * Normalize package properties, tags, revision id.\n * @param {Object} pkg package reference.\n */\nexport function normalizePackage(pkg: Manifest): Manifest {\n const pkgProperties = ['versions', 'dist-tags', '_distfiles', '_attachments', '_uplinks', 'time'];\n\n pkgProperties.forEach((key): void => {\n const pkgProp = pkg[key];\n\n if (isNil(pkgProp) || validationUtils.isObject(pkgProp) === false) {\n pkg[key] = {};\n }\n });\n\n if (typeof pkg?._rev !== 'string') {\n pkg._rev = STORAGE.DEFAULT_REVISION;\n }\n\n if (typeof pkg?._id !== 'string') {\n pkg._id = pkg.name;\n }\n\n // normalize dist-tags\n return normalizeDistTags(pkg);\n}\n\nexport function generateRevision(rev: string): string {\n const _rev = rev.split('-');\n\n return (+_rev[0] || 0) + 1 + '-' + cryptoUtils.generateRandomHexString();\n}\n\nexport function getLatestReadme(pkg: Manifest): string {\n const versions = pkg['versions'] || {};\n const distTags = pkg[DIST_TAGS] || {};\n // FIXME: here is a bit tricky add the types\n const latestVersion: Version | any = distTags['latest'] ? versions[distTags['latest']] || {} : {};\n let readme = trim(pkg.readme || latestVersion.readme || '');\n if (readme) {\n return readme;\n }\n\n // In case of empty readme - trying to get ANY readme in the following order:\n // 'next','beta','alpha','test','dev','canary'\n const readmeDistTagsPriority = ['next', 'beta', 'alpha', 'test', 'dev', 'canary'];\n readmeDistTagsPriority.forEach(function (tag): string | void {\n if (readme) {\n return readme;\n }\n const version: Version | any = distTags[tag] ? versions[distTags[tag]] || {} : {};\n readme = trim(version.readme || readme);\n });\n return readme;\n}\n\n/**\n * Cleanup readme from package version\n *\n * By default, we don't keep readmes for package versions, only one readme per package.\n * Using publish.keep_readmes you can override this behavior and keep all readmes\n * or only readmes for tagged versions.\n */\nexport function cleanUpReadme(\n version: Version,\n distTags?: GenericBody,\n keepReadmes?: ReadmeOptions\n): Version {\n if (keepReadmes === 'all') {\n return version;\n } else if (keepReadmes === 'tagged') {\n if (distTags && Object.values(distTags).includes(version.version)) {\n return version;\n }\n }\n\n if (isNil(version) === false) {\n version.readme = '';\n }\n\n return version;\n}\n\nexport const WHITELIST = [\n '_rev',\n 'name',\n 'versions',\n 'dist-tags',\n 'readme',\n 'time',\n '_id',\n 'users',\n 'maintainers',\n];\n\nexport function cleanUpLinksRef(result: Manifest, keepUpLinkData?: boolean): Manifest {\n const propertyToKeep = [...WHITELIST];\n if (keepUpLinkData === true) {\n propertyToKeep.push('_uplinks');\n }\n\n for (const i in result) {\n if (propertyToKeep.indexOf(i) === -1) {\n // Remove sections like '_uplinks' from response\n delete result[i];\n }\n }\n\n return result;\n}\n\n// TODO: move to abstract storage\n// @deprecated use abstract.storage.ts:checkPackageRemote\nexport function checkPackageRemote(\n name: string,\n isAllowPublishOffline: boolean,\n syncMetadata: Function\n): Promise<any> {\n return new Promise<void>((resolve, reject): void => {\n syncMetadata(name, null, {}, (err, packageJsonLocal, upLinksErrors): void => {\n // something weird\n if (err && err.status !== HTTP_STATUS.NOT_FOUND) {\n return reject(err);\n }\n\n // checking package exist already\n if (isNil(packageJsonLocal) === false) {\n return reject(errorUtils.getConflict(API_ERROR.PACKAGE_EXIST));\n }\n\n for (let errorItem = 0; errorItem < upLinksErrors.length; errorItem++) {\n // checking error\n // if uplink fails with a status other than 404, we report failure\n if (isNil(upLinksErrors[errorItem][0]) === false) {\n if (upLinksErrors[errorItem][0].status !== HTTP_STATUS.NOT_FOUND) {\n if (isAllowPublishOffline) {\n return resolve();\n }\n\n return reject(errorUtils.getServiceUnavailable(API_ERROR.UPLINK_OFFLINE_PUBLISH));\n }\n }\n }\n\n return resolve();\n });\n });\n}\n\n// @deprecated use export function mergeUplinkTimeIntoLocalNext\nexport function mergeUplinkTimeIntoLocal(localMetadata: Manifest, remoteMetadata: Manifest): any {\n if ('time' in remoteMetadata) {\n return Object.assign({}, localMetadata.time, remoteMetadata.time);\n }\n\n return localMetadata.time;\n}\n\nexport function mergeUplinkTimeIntoLocalNext(\n cacheManifest: Manifest,\n remoteManifest: Manifest\n): Manifest {\n if ('time' in remoteManifest) {\n // remote override cache time conflicts\n return { ...cacheManifest, time: { ...cacheManifest.time, ...remoteManifest.time } };\n }\n\n return cacheManifest;\n}\n\nexport function updateUpLinkMetadata(uplinkName: string, manifest: Manifest, etag: string) {\n const _uplinks = {\n ...manifest._uplinks,\n [uplinkName]: {\n etag,\n fetched: Date.now(),\n },\n };\n return {\n ...manifest,\n _uplinks,\n };\n}\n\nexport function prepareSearchPackage(data: Manifest): any {\n const latest = pkgUtils.getLatest(data);\n\n if (latest && data.versions[latest]) {\n const version: Version = data.versions[latest];\n const versions: any = { [latest]: 'latest' };\n const pkg: any = {\n name: version.name,\n description: version.description,\n [DIST_TAGS]: { latest },\n maintainers: version.maintainers || [version.author].filter(Boolean),\n author: version.author,\n repository: version.repository,\n readmeFilename: version.readmeFilename || '',\n homepage: version.homepage,\n keywords: version.keywords,\n bugs: version.bugs,\n license: version.license,\n // time: {\n // modified: time,\n // },\n versions,\n };\n\n return pkg;\n }\n}\n\n/**\n * Function gets a local info and an info from uplinks and tries to merge it\n exported for unit tests only.\n * @param {*} cacheManifest\n * @param {*} remoteManifest\n */\nexport function mergeVersions(cacheManifest: Manifest, remoteManifest: Manifest): Manifest {\n let _cacheManifest = { ...cacheManifest };\n const { versions, time } = remoteManifest;\n // copy new versions to a cache\n // NOTE: if a certain version was updated, we can't refresh it reliably\n // however, we can refresh the deprecated status\n for (const i in versions) {\n if (typeof _cacheManifest.versions[i] === 'undefined') {\n _cacheManifest.versions[i] = versions[i];\n } else if (versions[i].deprecated) {\n _cacheManifest.versions[i].deprecated = versions[i].deprecated;\n }\n }\n\n for (const i in time) {\n if (typeof _cacheManifest.time[i] === 'undefined') {\n _cacheManifest.time[i] = time[i];\n }\n }\n\n for (const distTag in remoteManifest[DIST_TAGS]) {\n if (_cacheManifest[DIST_TAGS][distTag] !== remoteManifest[DIST_TAGS][distTag]) {\n if (\n !_cacheManifest[DIST_TAGS][distTag] ||\n semver.lte(_cacheManifest[DIST_TAGS][distTag], remoteManifest[DIST_TAGS][distTag])\n ) {\n _cacheManifest[DIST_TAGS][distTag] = remoteManifest[DIST_TAGS][distTag];\n }\n if (\n distTag === 'latest' &&\n _cacheManifest[DIST_TAGS][distTag] === remoteManifest[DIST_TAGS][distTag]\n ) {\n // NOTE: this override the latest publish readme from local cache with\n // the remote one\n _cacheManifest = { ..._cacheManifest, readme: remoteManifest.readme };\n }\n }\n }\n return _cacheManifest;\n}\n\n/**\n * Normalize dist-tags.\n *\n * There is a legacy behaviour where the dist-tags could be an array, in such\n * case, the array is orderded and the highest version becames the\n * latest.\n *\n * The dist-tag tags must be plain strigs, if the latest is empty (for whatever reason) is\n * normalized to be the highest version available.\n *\n * This function cleans up every invalid version on dist-tags, but does not remove\n * invalid versions from the manifest.\n *\n * @param {*} data\n */\nexport function normalizeDistTags(manifest: Manifest): Manifest {\n let sorted;\n // handle missing latest dist-tag\n if (!manifest[DIST_TAGS].latest) {\n // if there is no latest tag, set the highest known version based on semver sort\n sorted = sortVersionsAndFilterInvalid(Object.keys(manifest.versions));\n if (sorted?.length) {\n // get the highest published version\n manifest[DIST_TAGS].latest = sorted.pop();\n }\n }\n\n for (const tag in manifest[DIST_TAGS]) {\n // deprecated (will be removed un future majors)\n // this should not happen, tags should be plain strings, legacy fallback\n if (Array.isArray(manifest[DIST_TAGS][tag])) {\n if (manifest[DIST_TAGS][tag].length) {\n // sort array\n // FIXME: this is clearly wrong, we need to research why this is like this.\n // @ts-ignore\n sorted = sortVersionsAndFilterInvalid(manifest[DIST_TAGS][tag]);\n if (sorted.length) {\n // use highest version based on semver sort\n manifest[DIST_TAGS][tag] = sorted.pop();\n }\n } else {\n delete manifest[DIST_TAGS][tag];\n }\n } else if (typeof manifest[DIST_TAGS][tag] === 'string') {\n if (!semver.parse(manifest[DIST_TAGS][tag], true)) {\n // if the version is invalid, delete the dist-tag entry\n delete manifest[DIST_TAGS][tag];\n }\n }\n }\n\n return manifest;\n}\n\nexport function hasDeprecatedVersions(pkgInfo: Manifest): boolean {\n const { versions } = pkgInfo;\n for (const version in versions) {\n if (Object.prototype.hasOwnProperty.call(versions[version], 'deprecated')) {\n return true;\n }\n }\n return false;\n}\n\nexport function isDeprecatedManifest(manifest: Manifest): boolean {\n return (\n hasDeprecatedVersions(manifest) &&\n (typeof manifest._attachments === 'undefined' ||\n Object.keys(manifest._attachments).length === 0)\n );\n}\n\nexport function mapManifestToSearchPackageBody(\n pkg: Manifest,\n searchItem: searchUtils.SearchItem\n): searchUtils.SearchPackageBody {\n const latest = pkgUtils.getLatest(pkg);\n const version: Version = pkg.versions[latest];\n // packument maintainers carry `name`, but the npm search API contract uses\n // `username` and the npm CLI crashes on entries without it — emit both\n const maintainers = Array.isArray(version.maintainers)\n ? version.maintainers.map((maintainer: any) =>\n typeof maintainer === 'string'\n ? { username: maintainer, email: '' }\n : {\n ...maintainer,\n username: maintainer?.username ?? maintainer?.name ?? '',\n email: maintainer?.email ?? '',\n }\n )\n : [];\n // npmjs fills `publisher` with the user who published the version; use\n // `_npmUser` when the client provided it and fall back to the first\n // maintainer (verdaccio sets it to the publishing user), otherwise the\n // npm CLI renders \"published by ???\"\n const publisher = version._npmUser?.name\n ? { username: version._npmUser.name, email: version._npmUser.email ?? '' }\n : (maintainers[0] ?? {});\n const result: searchUtils.SearchPackageBody = {\n name: version.name,\n scope: '',\n description: version.description,\n version: latest,\n keywords: version.keywords,\n date: pkg.time[latest],\n // FIXME: type\n author: version.author as any,\n publisher,\n maintainers,\n links: {\n npm: '',\n homepage: version.homepage,\n repository: version.repository,\n bugs: version.bugs,\n },\n };\n\n if (typeof searchItem.package.scoped === 'string') {\n result.scope = searchItem.package.scoped;\n }\n\n return result;\n}\n\nexport function normalizeContributors(contributors: Author[]): Author[] {\n if (isNil(contributors)) {\n return [];\n } else if (contributors && !Array.isArray(contributors)) {\n return [contributors];\n } else if (typeof contributors === 'string') {\n return [\n {\n name: contributors,\n },\n ];\n }\n\n return contributors;\n}\n"],"mappings":";;;;;AAmBA,IAAa,UAAU;CACrB,mBAAmB;CACnB,kBAAkB;CAClB,oBAAoB;CACpB,kBAAkB;AACpB;;;;;;AAOA,SAAgB,wBAAwB,MAAwB;CAC9D,OAAO;EAEL;EACA,UAAU,CAAC;EACX,MAAM,CAAC;GACN,QAAQ,CAAC;GACT,YAAY,CAAC;GACb,cAAc,CAAC;EAChB,UAAU,CAAC;EACX,YAAY,CAAC;EACb,cAAc,CAAC;EACf,MAAM;CACR;AACF;;;;;AAMA,SAAgB,iBAAiB,KAAyB;CAGxD;EAFuB;EAAY;EAAa;EAAc;EAAgB;EAAY;CAE1F,CAAA,CAAc,SAAS,QAAc;EACnC,MAAM,UAAU,IAAI;EAEpB,IAAI,MAAM,OAAO,KAAK,gBAAgB,SAAS,OAAO,MAAM,OAC1D,IAAI,OAAO,CAAC;CAEhB,CAAC;CAED,IAAI,OAAO,KAAK,SAAS,UACvB,IAAI,OAAO,QAAQ;CAGrB,IAAI,OAAO,KAAK,QAAQ,UACtB,IAAI,MAAM,IAAI;CAIhB,OAAO,kBAAkB,GAAG;AAC9B;AAEA,SAAgB,iBAAiB,KAAqB;CAGpD,QAAQ,CAFK,IAAI,MAAM,GAEd,CAAA,CAAK,MAAM,KAAK,IAAI,MAAM,YAAY,wBAAwB;AACzE;AAEA,SAAgB,gBAAgB,KAAuB;CACrD,MAAM,WAAW,IAAI,eAAe,CAAC;CACrC,MAAM,WAAW,IAAI,cAAc,CAAC;CAEpC,MAAM,gBAA+B,SAAS,YAAY,SAAS,SAAS,cAAc,CAAC,IAAI,CAAC;CAChG,IAAI,SAAS,KAAK,IAAI,UAAU,cAAc,UAAU,EAAE;CAC1D,IAAI,QACF,OAAO;CAMT;EADgC;EAAQ;EAAQ;EAAS;EAAQ;EAAO;CACxE,CAAA,CAAuB,QAAQ,SAAU,KAAoB;EAC3D,IAAI,QACF,OAAO;EAET,MAAM,UAAyB,SAAS,OAAO,SAAS,SAAS,SAAS,CAAC,IAAI,CAAC;EAChF,SAAS,KAAK,QAAQ,UAAU,MAAM;CACxC,CAAC;CACD,OAAO;AACT;;;;;;;;AASA,SAAgB,cACd,SACA,UACA,aACS;CACT,IAAI,gBAAgB,OAClB,OAAO;MACF,IAAI,gBAAgB,UACrB;MAAA,YAAY,OAAO,OAAO,QAAQ,CAAC,CAAC,SAAS,QAAQ,OAAO,GAC9D,OAAO;CAAA;CAIX,IAAI,MAAM,OAAO,MAAM,OACrB,QAAQ,SAAS;CAGnB,OAAO;AACT;AAEA,IAAa,YAAY;CACvB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAgB,gBAAgB,QAAkB,gBAAoC;CACpF,MAAM,iBAAiB,CAAC,GAAG,SAAS;CACpC,IAAI,mBAAmB,MACrB,eAAe,KAAK,UAAU;CAGhC,KAAK,MAAM,KAAK,QACd,IAAI,eAAe,QAAQ,CAAC,MAAM,IAEhC,OAAO,OAAO;CAIlB,OAAO;AACT;AAIA,SAAgB,mBACd,MACA,uBACA,cACc;CACd,OAAO,IAAI,SAAe,SAAS,WAAiB;EAClD,aAAa,MAAM,MAAM,CAAC,IAAI,KAAK,kBAAkB,kBAAwB;GAE3E,IAAI,OAAO,IAAI,WAAW,YAAY,WACpC,OAAO,OAAO,GAAG;GAInB,IAAI,MAAM,gBAAgB,MAAM,OAC9B,OAAO,OAAO,WAAW,YAAY,UAAU,aAAa,CAAC;GAG/D,KAAK,IAAI,YAAY,GAAG,YAAY,cAAc,QAAQ,aAGxD,IAAI,MAAM,cAAc,UAAU,CAAC,EAAE,MAAM,OACrC;QAAA,cAAc,UAAU,CAAC,EAAE,CAAC,WAAW,YAAY,WAAW;KAChE,IAAI,uBACF,OAAO,QAAQ;KAGjB,OAAO,OAAO,WAAW,sBAAsB,UAAU,sBAAsB,CAAC;IAClF;;GAIJ,OAAO,QAAQ;EACjB,CAAC;CACH,CAAC;AACH;AAGA,SAAgB,yBAAyB,eAAyB,gBAA+B;CAC/F,IAAI,UAAU,gBACZ,OAAO,OAAO,OAAO,CAAC,GAAG,cAAc,MAAM,eAAe,IAAI;CAGlE,OAAO,cAAc;AACvB;AAEA,SAAgB,6BACd,eACA,gBACU;CACV,IAAI,UAAU,gBAEZ,OAAO;EAAE,GAAG;EAAe,MAAM;GAAE,GAAG,cAAc;GAAM,GAAG,eAAe;EAAK;CAAE;CAGrF,OAAO;AACT;AAEA,SAAgB,qBAAqB,YAAoB,UAAoB,MAAc;CACzF,MAAM,WAAW;EACf,GAAG,SAAS;GACX,aAAa;GACZ;GACA,SAAS,KAAK,IAAI;EACpB;CACF;CACA,OAAO;EACL,GAAG;EACH;CACF;AACF;AAEA,SAAgB,qBAAqB,MAAqB;CACxD,MAAM,SAAS,SAAS,UAAU,IAAI;CAEtC,IAAI,UAAU,KAAK,SAAS,SAAS;EACnC,MAAM,UAAmB,KAAK,SAAS;EACvC,MAAM,WAAgB,GAAG,SAAS,SAAS;EAmB3C,OAAO;GAjBL,MAAM,QAAQ;GACd,aAAa,QAAQ;IACpB,YAAY,EAAE,OAAO;GACtB,aAAa,QAAQ,eAAe,CAAC,QAAQ,MAAM,CAAC,CAAC,OAAO,OAAO;GACnE,QAAQ,QAAQ;GAChB,YAAY,QAAQ;GACpB,gBAAgB,QAAQ,kBAAkB;GAC1C,UAAU,QAAQ;GAClB,UAAU,QAAQ;GAClB,MAAM,QAAQ;GACd,SAAS,QAAQ;GAIjB;EAGK;CACT;AACF;;;;;;;AAQA,SAAgB,cAAc,eAAyB,gBAAoC;CACzF,IAAI,iBAAiB,EAAE,GAAG,cAAc;CACxC,MAAM,EAAE,UAAU,SAAS;CAI3B,KAAK,MAAM,KAAK,UACd,IAAI,OAAO,eAAe,SAAS,OAAO,aACxC,eAAe,SAAS,KAAK,SAAS;MACjC,IAAI,SAAS,EAAE,CAAC,YACrB,eAAe,SAAS,EAAE,CAAC,aAAa,SAAS,EAAE,CAAC;CAIxD,KAAK,MAAM,KAAK,MACd,IAAI,OAAO,eAAe,KAAK,OAAO,aACpC,eAAe,KAAK,KAAK,KAAK;CAIlC,KAAK,MAAM,WAAW,eAAe,YACnC,IAAI,eAAe,UAAU,CAAC,aAAa,eAAe,UAAU,CAAC,UAAU;EAC7E,IACE,CAAC,eAAe,UAAU,CAAC,YAC3B,OAAO,IAAI,eAAe,UAAU,CAAC,UAAU,eAAe,UAAU,CAAC,QAAQ,GAEjF,eAAe,UAAU,CAAC,WAAW,eAAe,UAAU,CAAC;EAEjE,IACE,YAAY,YACZ,eAAe,UAAU,CAAC,aAAa,eAAe,UAAU,CAAC,UAIjE,iBAAiB;GAAE,GAAG;GAAgB,QAAQ,eAAe;EAAO;CAExE;CAEF,OAAO;AACT;;;;;;;;;;;;;;;;AAiBA,SAAgB,kBAAkB,UAA8B;CAC9D,IAAI;CAEJ,IAAI,CAAC,SAAS,UAAU,CAAC,QAAQ;EAE/B,SAAS,6BAA6B,OAAO,KAAK,SAAS,QAAQ,CAAC;EACpE,IAAI,QAAQ,QAEV,SAAS,UAAU,CAAC,SAAS,OAAO,IAAI;CAE5C;CAEA,KAAK,MAAM,OAAO,SAAS,YAGzB,IAAI,MAAM,QAAQ,SAAS,UAAU,CAAC,IAAI,GAAG;EAC3C,IAAI,SAAS,UAAU,CAAC,IAAI,CAAC,QAAQ;GAInC,SAAS,6BAA6B,SAAS,UAAU,CAAC,IAAI;GAC9D,IAAI,OAAO,QAET,SAAS,UAAU,CAAC,OAAO,OAAO,IAAI;EAE1C,OACE,OAAO,SAAS,UAAU,CAAC;CAE/B,OAAO,IAAI,OAAO,SAAS,UAAU,CAAC,SAAS,UACzC;MAAA,CAAC,OAAO,MAAM,SAAS,UAAU,CAAC,MAAM,IAAI,GAE9C,OAAO,SAAS,UAAU,CAAC;CAAA;CAKjC,OAAO;AACT;AAEA,SAAgB,sBAAsB,SAA4B;CAChE,MAAM,EAAE,aAAa;CACrB,KAAK,MAAM,WAAW,UACpB,IAAI,OAAO,UAAU,eAAe,KAAK,SAAS,UAAU,YAAY,GACtE,OAAO;CAGX,OAAO;AACT;AAEA,SAAgB,qBAAqB,UAA6B;CAChE,OACE,sBAAsB,QAAQ,MAC7B,OAAO,SAAS,iBAAiB,eAChC,OAAO,KAAK,SAAS,YAAY,CAAC,CAAC,WAAW;AAEpD;AAEA,SAAgB,+BACd,KACA,YAC+B;CAC/B,MAAM,SAAS,SAAS,UAAU,GAAG;CACrC,MAAM,UAAmB,IAAI,SAAS;CAGtC,MAAM,cAAc,MAAM,QAAQ,QAAQ,WAAW,IACjD,QAAQ,YAAY,KAAK,eACvB,OAAO,eAAe,WAClB;EAAE,UAAU;EAAY,OAAO;CAAG,IAClC;EACE,GAAG;EACH,UAAU,YAAY,YAAY,YAAY,QAAQ;EACtD,OAAO,YAAY,SAAS;CAC9B,CACN,IACA,CAAC;CAKL,MAAM,YAAY,QAAQ,UAAU,OAChC;EAAE,UAAU,QAAQ,SAAS;EAAM,OAAO,QAAQ,SAAS,SAAS;CAAG,IACtE,YAAY,MAAM,CAAC;CACxB,MAAM,SAAwC;EAC5C,MAAM,QAAQ;EACd,OAAO;EACP,aAAa,QAAQ;EACrB,SAAS;EACT,UAAU,QAAQ;EAClB,MAAM,IAAI,KAAK;EAEf,QAAQ,QAAQ;EAChB;EACA;EACA,OAAO;GACL,KAAK;GACL,UAAU,QAAQ;GAClB,YAAY,QAAQ;GACpB,MAAM,QAAQ;EAChB;CACF;CAEA,IAAI,OAAO,WAAW,QAAQ,WAAW,UACvC,OAAO,QAAQ,WAAW,QAAQ;CAGpC,OAAO;AACT;AAEA,SAAgB,sBAAsB,cAAkC;CACtE,IAAI,MAAM,YAAY,GACpB,OAAO,CAAC;MACH,IAAI,gBAAgB,CAAC,MAAM,QAAQ,YAAY,GACpD,OAAO,CAAC,YAAY;MACf,IAAI,OAAO,iBAAiB,UACjC,OAAO,CACL,EACE,MAAM,aACR,CACF;CAGF,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"storage-utils.mjs","names":[],"sources":["../../src/lib/storage-utils.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { isNil, trim } from 'lodash-es';\nimport semver from 'semver';\n\nimport type { searchUtils } from '@verdaccio/core';\nimport {\n API_ERROR,\n DIST_TAGS,\n HTTP_STATUS,\n MAINTAINERS,\n USERS,\n cryptoUtils,\n errorUtils,\n pkgUtils,\n validationUtils,\n} from '@verdaccio/core';\nimport type {\n Author,\n DistFile,\n GenericBody,\n Manifest,\n ReadmeOptions,\n Version,\n} from '@verdaccio/types';\n\nimport { sortVersionsAndFilterInvalid } from './versions-utils';\n\nconst debug = buildDebug('verdaccio:storage:utils');\n\nexport const STORAGE = {\n PACKAGE_FILE_NAME: 'package.json',\n FILE_EXIST_ERROR: 'EEXISTS',\n NO_SUCH_FILE_ERROR: 'ENOENT',\n DEFAULT_REVISION: '0-0000000000000000',\n};\n\n/**\n * Create a new package in the storage, return a boilerplate package\n * @param name package name\n * @returns {Manifest}\n */\nexport function generatePackageTemplate(name: string): Manifest {\n return {\n // standard things\n name,\n versions: {},\n time: {},\n [USERS]: {},\n [DIST_TAGS]: {},\n [MAINTAINERS]: [],\n _uplinks: {},\n _distfiles: {},\n _attachments: {},\n _rev: '',\n };\n}\n\n/**\n * Normalize package properties, tags, revision id.\n * @param {Object} pkg package reference.\n */\nexport function normalizePackage(pkg: Manifest): Manifest {\n const pkgProperties = ['versions', 'dist-tags', '_distfiles', '_attachments', '_uplinks', 'time'];\n\n pkgProperties.forEach((key): void => {\n const pkgProp = pkg[key];\n\n if (isNil(pkgProp) || validationUtils.isObject(pkgProp) === false) {\n pkg[key] = {};\n }\n });\n\n if (typeof pkg?._rev !== 'string') {\n pkg._rev = STORAGE.DEFAULT_REVISION;\n }\n\n if (typeof pkg?._id !== 'string') {\n pkg._id = pkg.name;\n }\n\n // normalize dist-tags\n return normalizeDistTags(pkg);\n}\n\nexport function generateRevision(rev: string): string {\n const _rev = rev.split('-');\n\n return (+_rev[0] || 0) + 1 + '-' + cryptoUtils.generateRandomHexString();\n}\n\nexport function getLatestReadme(pkg: Manifest): string {\n const versions = pkg['versions'] || {};\n const distTags = pkg[DIST_TAGS] || {};\n // FIXME: here is a bit tricky add the types\n const latestVersion: Version | any = distTags['latest'] ? versions[distTags['latest']] || {} : {};\n let readme = trim(pkg.readme || latestVersion.readme || '');\n if (readme) {\n return readme;\n }\n\n // In case of empty readme - trying to get ANY readme in the following order:\n // 'next','beta','alpha','test','dev','canary'\n const readmeDistTagsPriority = ['next', 'beta', 'alpha', 'test', 'dev', 'canary'];\n readmeDistTagsPriority.forEach(function (tag): string | void {\n if (readme) {\n return readme;\n }\n const version: Version | any = distTags[tag] ? versions[distTags[tag]] || {} : {};\n readme = trim(version.readme || readme);\n });\n return readme;\n}\n\n/**\n * Cleanup readme from package version\n *\n * By default, we don't keep readmes for package versions, only one readme per package.\n * Using publish.keep_readmes you can override this behavior and keep all readmes\n * or only readmes for tagged versions.\n */\nexport function cleanUpReadme(\n version: Version,\n distTags?: GenericBody,\n keepReadmes?: ReadmeOptions\n): Version {\n if (keepReadmes === 'all') {\n return version;\n } else if (keepReadmes === 'tagged') {\n if (distTags && Object.values(distTags).includes(version.version)) {\n return version;\n }\n }\n\n if (isNil(version) === false) {\n version.readme = '';\n }\n\n return version;\n}\n\nexport const WHITELIST = [\n '_rev',\n 'name',\n 'versions',\n 'dist-tags',\n 'readme',\n 'time',\n '_id',\n 'users',\n 'maintainers',\n];\n\nexport function cleanUpLinksRef(result: Manifest, keepUpLinkData?: boolean): Manifest {\n const propertyToKeep = [...WHITELIST];\n if (keepUpLinkData === true) {\n propertyToKeep.push('_uplinks');\n }\n\n for (const i in result) {\n if (propertyToKeep.indexOf(i) === -1) {\n // Remove sections like '_uplinks' from response\n delete result[i];\n }\n }\n\n return result;\n}\n\n// TODO: move to abstract storage\n// @deprecated use abstract.storage.ts:checkPackageRemote\nexport function checkPackageRemote(\n name: string,\n isAllowPublishOffline: boolean,\n syncMetadata: Function\n): Promise<any> {\n return new Promise<void>((resolve, reject): void => {\n syncMetadata(name, null, {}, (err, packageJsonLocal, upLinksErrors): void => {\n // something weird\n if (err && err.status !== HTTP_STATUS.NOT_FOUND) {\n return reject(err);\n }\n\n // checking package exist already\n if (isNil(packageJsonLocal) === false) {\n return reject(errorUtils.getConflict(API_ERROR.PACKAGE_EXIST));\n }\n\n for (let errorItem = 0; errorItem < upLinksErrors.length; errorItem++) {\n // checking error\n // if uplink fails with a status other than 404, we report failure\n if (isNil(upLinksErrors[errorItem][0]) === false) {\n if (upLinksErrors[errorItem][0].status !== HTTP_STATUS.NOT_FOUND) {\n if (isAllowPublishOffline) {\n return resolve();\n }\n\n return reject(errorUtils.getServiceUnavailable(API_ERROR.UPLINK_OFFLINE_PUBLISH));\n }\n }\n }\n\n return resolve();\n });\n });\n}\n\n// @deprecated use export function mergeUplinkTimeIntoLocalNext\nexport function mergeUplinkTimeIntoLocal(localMetadata: Manifest, remoteMetadata: Manifest): any {\n if ('time' in remoteMetadata) {\n return Object.assign({}, localMetadata.time, remoteMetadata.time);\n }\n\n return localMetadata.time;\n}\n\nexport function mergeUplinkTimeIntoLocalNext(\n cacheManifest: Manifest,\n remoteManifest: Manifest\n): Manifest {\n if ('time' in remoteManifest) {\n // remote override cache time conflicts\n return { ...cacheManifest, time: { ...cacheManifest.time, ...remoteManifest.time } };\n }\n\n return cacheManifest;\n}\n\nexport function updateUpLinkMetadata(uplinkName: string, manifest: Manifest, etag: string) {\n const _uplinks = {\n ...manifest._uplinks,\n [uplinkName]: {\n etag,\n fetched: Date.now(),\n },\n };\n return {\n ...manifest,\n _uplinks,\n };\n}\n\nexport function prepareSearchPackage(data: Manifest): any {\n const latest = pkgUtils.getLatest(data);\n\n if (latest && data.versions[latest]) {\n const version: Version = data.versions[latest];\n const versions: any = { [latest]: 'latest' };\n const pkg: any = {\n name: version.name,\n description: version.description,\n [DIST_TAGS]: { latest },\n maintainers: version.maintainers || [version.author].filter(Boolean),\n author: version.author,\n repository: version.repository,\n readmeFilename: version.readmeFilename || '',\n homepage: version.homepage,\n keywords: version.keywords,\n bugs: version.bugs,\n license: version.license,\n // time: {\n // modified: time,\n // },\n versions,\n };\n\n return pkg;\n }\n}\n\n/**\n * Function gets a local info and an info from uplinks and tries to merge it\n exported for unit tests only.\n * @param {*} cacheManifest\n * @param {*} remoteManifest\n */\nexport function mergeVersions(cacheManifest: Manifest, remoteManifest: Manifest): Manifest {\n let _cacheManifest = { ...cacheManifest };\n const { versions, time } = remoteManifest;\n // copy new versions to a cache\n // NOTE: if a certain version was updated, we can't refresh it reliably\n // however, we can refresh the deprecated status\n for (const i in versions) {\n if (typeof _cacheManifest.versions[i] === 'undefined') {\n _cacheManifest.versions[i] = versions[i];\n } else if (versions[i].deprecated) {\n _cacheManifest.versions[i].deprecated = versions[i].deprecated;\n }\n }\n\n for (const i in time) {\n if (typeof _cacheManifest.time[i] === 'undefined') {\n _cacheManifest.time[i] = time[i];\n }\n }\n\n for (const distTag in remoteManifest[DIST_TAGS]) {\n if (_cacheManifest[DIST_TAGS][distTag] !== remoteManifest[DIST_TAGS][distTag]) {\n if (\n !_cacheManifest[DIST_TAGS][distTag] ||\n semver.lte(_cacheManifest[DIST_TAGS][distTag], remoteManifest[DIST_TAGS][distTag])\n ) {\n _cacheManifest[DIST_TAGS][distTag] = remoteManifest[DIST_TAGS][distTag];\n }\n if (\n distTag === 'latest' &&\n _cacheManifest[DIST_TAGS][distTag] === remoteManifest[DIST_TAGS][distTag]\n ) {\n // NOTE: this override the latest publish readme from local cache with\n // the remote one\n _cacheManifest = { ..._cacheManifest, readme: remoteManifest.readme };\n }\n }\n }\n return _cacheManifest;\n}\n\n/**\n * Normalize dist-tags.\n *\n * There is a legacy behaviour where the dist-tags could be an array, in such\n * case, the array is orderded and the highest version becames the\n * latest.\n *\n * The dist-tag tags must be plain strigs, if the latest is empty (for whatever reason) is\n * normalized to be the highest version available.\n *\n * This function cleans up every invalid version on dist-tags, but does not remove\n * invalid versions from the manifest.\n *\n * @param {*} data\n */\nexport function normalizeDistTags(manifest: Manifest): Manifest {\n let sorted;\n // handle missing latest dist-tag\n if (!manifest[DIST_TAGS].latest) {\n // if there is no latest tag, set the highest known version based on semver sort\n sorted = sortVersionsAndFilterInvalid(Object.keys(manifest.versions));\n if (sorted?.length) {\n // get the highest published version\n manifest[DIST_TAGS].latest = sorted.pop();\n }\n }\n\n for (const tag in manifest[DIST_TAGS]) {\n // deprecated (will be removed un future majors)\n // this should not happen, tags should be plain strings, legacy fallback\n if (Array.isArray(manifest[DIST_TAGS][tag])) {\n if (manifest[DIST_TAGS][tag].length) {\n // sort array\n // FIXME: this is clearly wrong, we need to research why this is like this.\n // @ts-ignore\n sorted = sortVersionsAndFilterInvalid(manifest[DIST_TAGS][tag]);\n if (sorted.length) {\n // use highest version based on semver sort\n manifest[DIST_TAGS][tag] = sorted.pop();\n }\n } else {\n delete manifest[DIST_TAGS][tag];\n }\n } else if (typeof manifest[DIST_TAGS][tag] === 'string') {\n if (!semver.parse(manifest[DIST_TAGS][tag], true)) {\n // if the version is invalid, delete the dist-tag entry\n delete manifest[DIST_TAGS][tag];\n }\n }\n }\n\n return manifest;\n}\n\nexport function hasDeprecatedVersions(pkgInfo: Manifest): boolean {\n const { versions } = pkgInfo;\n for (const version in versions) {\n if (Object.prototype.hasOwnProperty.call(versions[version], 'deprecated')) {\n return true;\n }\n }\n return false;\n}\n\nexport function isDeprecatedManifest(manifest: Manifest): boolean {\n return (\n hasDeprecatedVersions(manifest) &&\n (typeof manifest._attachments === 'undefined' ||\n Object.keys(manifest._attachments).length === 0)\n );\n}\n\nexport function mapManifestToSearchPackageBody(\n pkg: Manifest,\n _searchItem: searchUtils.SearchItem\n): searchUtils.SearchPackageBody {\n const latest = pkgUtils.getLatest(pkg);\n const version: Version = pkg.versions[latest];\n // packument maintainers carry `name`, but the npm search API contract uses\n // `username` and the npm CLI crashes on entries without it — emit both\n const maintainers = Array.isArray(version.maintainers)\n ? version.maintainers.map((maintainer: any) =>\n typeof maintainer === 'string'\n ? { username: maintainer, email: '' }\n : {\n ...maintainer,\n username: maintainer?.username ?? maintainer?.name ?? '',\n email: maintainer?.email ?? '',\n }\n )\n : [];\n // npmjs fills `publisher` with the user who published the version; use\n // `_npmUser` when the client provided it and fall back to the first\n // maintainer (verdaccio sets it to the publishing user), otherwise the\n // npm CLI renders \"published by ???\"\n const publisher = version._npmUser?.name\n ? { username: version._npmUser.name, email: version._npmUser.email ?? '' }\n : (maintainers[0] ?? {});\n const links: NonNullable<searchUtils.SearchPackageBody['links']> = {};\n const repository =\n typeof version.repository === 'string' ? version.repository : version.repository?.url;\n const bugs = typeof version.bugs === 'string' ? version.bugs : version.bugs?.url;\n if (version.homepage) {\n links.homepage = version.homepage;\n }\n if (repository) {\n links.repository = repository;\n }\n if (bugs) {\n links.bugs = bugs;\n }\n const result: searchUtils.SearchPackageBody = {\n name: version.name,\n description: version.description,\n version: latest,\n license: version.license,\n keywords: version.keywords,\n date: pkg.time[latest],\n // FIXME: type\n author: version.author as any,\n publisher,\n maintainers,\n };\n\n if (Object.keys(links).length > 0) {\n result.links = links;\n }\n\n return result;\n}\n\nexport function normalizeContributors(contributors: Author[]): Author[] {\n if (isNil(contributors)) {\n return [];\n } else if (contributors && !Array.isArray(contributors)) {\n return [contributors];\n } else if (typeof contributors === 'string') {\n return [\n {\n name: contributors,\n },\n ];\n }\n\n return contributors;\n}\n\n/**\n * Whether the tarball url path ends with the given file name (any query\n * string or fragment is ignored, like the path parsing in\n * `tarballUtils.extractTarballFromUrl`, which creates the `_distfiles`\n * keys — if that parsing ever changes, this must change with it).\n */\nexport function tarballMatchesFilename(tarball: string, filename: string): boolean {\n return tarball.split(/[?#]/)[0].endsWith(`/${filename}`);\n}\n\n/**\n * Build a distfile record from a version's dist metadata when its tarball\n * url matches the given file name.\n */\nexport function distFileFromVersion(\n version: Version | undefined,\n filename: string\n): DistFile | null {\n const dist = version?.dist;\n\n if (dist?.tarball && tarballMatchesFilename(dist.tarball, filename)) {\n return { url: dist.tarball, sha: dist.shasum } as DistFile;\n }\n\n return null;\n}\n\n/**\n * Resolve the distfile record for a tarball filename. Prefers the\n * `_distfiles` bookkeeping, but falls back to the version metadata when the\n * record is missing (storages written by other verdaccio versions cache\n * versions without their distfile records, which made those tarballs\n * permanently unavailable).\n *\n * Note: no extension pre-filter before the scan on purpose — GitHub\n * Packages advertises tarball urls ending in a bare sha-256 digest.\n * @param manifest cached manifest\n * @param filename tarball file name, eg. pkg-1.0.0.tgz\n */\nexport function lookupDistFile(manifest: Manifest, filename: string): DistFile | null {\n const distFile = manifest?._distfiles?.[filename];\n if (isNil(distFile) === false) {\n return distFile;\n }\n\n const versions = manifest?.versions ?? {};\n\n // tarballs are conventionally named <basename>-<version>.tgz, so the\n // version can usually be derived from the filename without scanning\n // manifests that carry thousands of versions\n const packageBasename = manifest?.name?.replace(/^.*\\//, '');\n if (packageBasename && filename.startsWith(`${packageBasename}-`) && filename.endsWith('.tgz')) {\n const versionId = filename.slice(packageBasename.length + 1, -'.tgz'.length);\n const distFromVersion = distFileFromVersion(versions[versionId], filename);\n\n if (distFromVersion) {\n debug('distfile record missing for %o, using version %o dist', filename, versionId);\n return distFromVersion;\n }\n }\n\n // unconventional tarball names: fall back to a scan\n for (const versionId in versions) {\n const distFromVersion = distFileFromVersion(versions[versionId], filename);\n\n if (distFromVersion) {\n debug('distfile record missing for %o, using version %o dist', filename, versionId);\n return distFromVersion;\n }\n }\n\n debug('no distfile record or version dist found for %o', filename);\n return null;\n}\n\n/**\n * Whether an uplink's configured url serves the given tarball url: same\n * protocol, same host and the uplink path is a prefix of the tarball path.\n * WHATWG URL normalizes default ports (https://h:443 === https://h).\n */\nexport function uplinkServesUrl(uplinkUrl: URL, tarballUrl: string): boolean {\n try {\n const parsed = new URL(tarballUrl);\n // Compare on a path segment boundary: an uplink at /registry must not\n // match tarballs under /registry2/... (wrong-uplink selection would send\n // the uplink's credentials to a sibling path).\n const basePath = uplinkUrl.pathname.endsWith('/')\n ? uplinkUrl.pathname\n : `${uplinkUrl.pathname}/`;\n return (\n parsed.protocol === uplinkUrl.protocol &&\n parsed.host === uplinkUrl.host &&\n parsed.pathname.startsWith(basePath)\n );\n } catch {\n return false;\n }\n}\n"],"mappings":"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"}
|
|
@@ -28,12 +28,12 @@ export declare function tagVersion(data: Manifest, version: string, tag: StringV
|
|
|
28
28
|
*/
|
|
29
29
|
export declare function tagVersionNext(manifest: Manifest, version: string, tag: StringValue): Manifest;
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
31
|
+
* Whether `newVersion` is strictly newer than `oldVersion` under loose semver.
|
|
32
|
+
* Equal versions in any spelling (`1.2.3`, `v1.2.3`, `01.2.3`, `1.2.3+build`) are
|
|
33
|
+
* not newer, so search merges keep the entry they saw first, which is the local one.
|
|
34
|
+
* Both arguments must already pass `pkgUtils.isValidVersion`.
|
|
35
35
|
*/
|
|
36
|
-
export declare function isNewerVersion(newVersion:
|
|
36
|
+
export declare function isNewerVersion(newVersion: string, oldVersion: string): boolean;
|
|
37
37
|
/**
|
|
38
38
|
* Remove duplicates from search results.
|
|
39
39
|
* @param {Array} objects
|