@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 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, searchItem: searchUtils.SearchItem): searchUtils.SearchPackageBody;
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, searchItem) {
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 (typeof searchItem.package.scoped === "string") result.scope = searchItem.package.scoped;
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, searchItem) {
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 (typeof searchItem.package.scoped === "string") result.scope = searchItem.package.scoped;
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":"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"}
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
- * Check if the version is newer than the older version.
32
- * @param newVersion
33
- * @param oldVersion
34
- * @returns
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: any, oldVersion: any): boolean;
36
+ export declare function isNewerVersion(newVersion: string, oldVersion: string): boolean;
37
37
  /**
38
38
  * Remove duplicates from search results.
39
39
  * @param {Array} objects