@verdaccio/store 9.0.0-next-9.31 → 9.0.0-next-9.33

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.
@@ -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
@@ -58,14 +58,13 @@ function tagVersionNext(manifest, version, tag) {
58
58
  return data;
59
59
  }
60
60
  /**
61
- * Check if the version is newer than the older version.
62
- * @param newVersion
63
- * @param oldVersion
64
- * @returns
61
+ * Whether `newVersion` is strictly newer than `oldVersion` under loose semver.
62
+ * Equal versions in any spelling (`1.2.3`, `v1.2.3`, `01.2.3`, `1.2.3+build`) are
63
+ * not newer, so search merges keep the entry they saw first, which is the local one.
64
+ * Both arguments must already pass `pkgUtils.isValidVersion`.
65
65
  */
66
66
  function isNewerVersion(newVersion, oldVersion) {
67
- const comparisonResult = semver.default.compare(newVersion, oldVersion);
68
- return comparisonResult === 1 || comparisonResult === 0;
67
+ return semver.default.compareLoose(newVersion, oldVersion) === 1;
69
68
  }
70
69
  /**
71
70
  * Remove duplicates from search results.
@@ -74,7 +73,13 @@ function isNewerVersion(newVersion, oldVersion) {
74
73
  */
75
74
  function removeLowerVersions(objects) {
76
75
  const versionMap = /* @__PURE__ */ new Map();
77
- objects.forEach((item) => {
76
+ const validObjects = objects.filter((item) => {
77
+ const version = item?.package?.version;
78
+ const valid = _verdaccio_core.pkgUtils.isValidVersion(version);
79
+ if (!valid) debug$1("ignoring invalid search version %o", version);
80
+ return valid;
81
+ });
82
+ validObjects.forEach((item) => {
78
83
  const { name, version } = item.package;
79
84
  const key = name;
80
85
  if (versionMap.has(name) === false || isNewerVersion(version, versionMap.get(name))) {
@@ -82,7 +87,7 @@ function removeLowerVersions(objects) {
82
87
  versionMap.set(key, version);
83
88
  }
84
89
  });
85
- return objects.reduce((acc, item) => {
90
+ return validObjects.reduce((acc, item) => {
86
91
  const { name, version } = item.package;
87
92
  if (versionMap.has(name) && versionMap.get(name) === version && acc.find((i) => i.package.name === name) === void 0) {
88
93
  debug$1("adding %o@%o", name, version);
@@ -1 +1 @@
1
- {"version":3,"file":"versions-utils.js","names":[],"sources":["../../src/lib/versions-utils.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { isNil } from 'lodash-es';\nimport type { SemVer } from 'semver';\nimport semver from 'semver';\n\nimport type { searchUtils } from '@verdaccio/core';\nimport { DIST_TAGS } from '@verdaccio/core';\nimport type { Manifest, StringValue, Version, Versions } from '@verdaccio/types';\n\nconst debug = buildDebug('verdaccio:storage:utils');\n\n/**\n * Gets version from a package object taking into account semver weirdness.\n * @return {String} return the semantic version of a package\n */\nexport function getVersion(versions: Versions, version: string): Version | undefined {\n if (!versions) {\n return;\n }\n\n // this condition must allow cast\n if (isNil(versions[version]) === false) {\n return versions[version];\n }\n\n const versionSemver: SemVer | null = semver.parse(version, true);\n if (versionSemver === null) {\n return;\n }\n\n for (const versionItem in versions) {\n if (Object.prototype.hasOwnProperty.call(versions, versionItem)) {\n // @ts-ignore\n if (versionSemver.compare(semver.parse(versionItem, true)) === 0) {\n return versions[versionItem];\n }\n }\n }\n}\n\n/**\n * Function filters out bad semver versions and sorts the array.\n * @return {Array} sorted Array\n */\nexport function sortVersionsAndFilterInvalid(listVersions: string[] /* logger */): string[] {\n return listVersions\n .filter(function (version): boolean {\n if (!semver.parse(version, true)) {\n return false;\n }\n return true;\n })\n .sort(semver.compareLoose)\n .map(String);\n}\n\n/**\n * Create a tag for a package\n * @param {*} data\n * @param {*} version\n * @param {*} tag\n * @return {Boolean} whether a package has been tagged\n * @deprecated\n */\nexport function tagVersion(data: Manifest, version: string, tag: StringValue): boolean {\n if (tag && data[DIST_TAGS][tag] !== version && semver.parse(version, true)) {\n // valid version - store\n data[DIST_TAGS][tag] = version;\n return true;\n }\n return false;\n}\n\n/**\n *\n * @param manifest\n * @param version\n * @param tag\n * @returns\n */\nexport function tagVersionNext(manifest: Manifest, version: string, tag: StringValue): Manifest {\n const data = { ...manifest };\n if (tag && data[DIST_TAGS][tag] !== version && semver.parse(version, true)) {\n // valid version - store\n data[DIST_TAGS][tag] = version;\n }\n return data;\n}\n\n/**\n * Check if the version is newer than the older version.\n * @param newVersion\n * @param oldVersion\n * @returns\n */\nexport function isNewerVersion(newVersion, oldVersion) {\n const comparisonResult = semver.compare(newVersion, oldVersion);\n\n return comparisonResult === 1 || comparisonResult === 0;\n}\n\n/**\n * Remove duplicates from search results.\n * @param {Array} objects\n * @return {Array} filtered array\n */\nexport function removeLowerVersions(objects: searchUtils.SearchPackageItem[]) {\n const versionMap = new Map();\n\n // Iterate through the array and keep the highest version for each name\n objects.forEach((item) => {\n const { name, version } = item.package;\n const key = name;\n\n if (versionMap.has(name) === false || isNewerVersion(version, versionMap.get(name))) {\n debug('keeping %o@%o', name, version);\n versionMap.set(key, version);\n }\n });\n\n // Filter objects based on the version map\n return objects.reduce((acc, item) => {\n const { name, version } = item.package;\n if (\n versionMap.has(name) &&\n versionMap.get(name) === version &&\n acc.find((i) => i.package.name === name) === undefined\n ) {\n debug('adding %o@%o', name, version);\n acc.push(item);\n }\n return acc;\n }, [] as searchUtils.SearchPackageItem[]);\n}\n"],"mappings":";;;;;;;;AASA,IAAM,WAAA,GAAQ,MAAA,QAAA,CAAW,yBAAyB;;;;;AAMlD,SAAgB,WAAW,UAAoB,SAAsC;CACnF,IAAI,CAAC,UACH;CAIF,KAAA,GAAI,UAAA,MAAA,CAAM,SAAS,QAAQ,MAAM,OAC/B,OAAO,SAAS;CAGlB,MAAM,gBAA+B,OAAA,QAAO,MAAM,SAAS,IAAI;CAC/D,IAAI,kBAAkB,MACpB;CAGF,KAAK,MAAM,eAAe,UACxB,IAAI,OAAO,UAAU,eAAe,KAAK,UAAU,WAAW,GAExD;MAAA,cAAc,QAAQ,OAAA,QAAO,MAAM,aAAa,IAAI,CAAC,MAAM,GAC7D,OAAO,SAAS;CAAA;AAIxB;;;;;AAMA,SAAgB,6BAA6B,cAA+C;CAC1F,OAAO,aACJ,OAAO,SAAU,SAAkB;EAClC,IAAI,CAAC,OAAA,QAAO,MAAM,SAAS,IAAI,GAC7B,OAAO;EAET,OAAO;CACT,CAAC,CAAC,CACD,KAAK,OAAA,QAAO,YAAY,CAAC,CACzB,IAAI,MAAM;AACf;;;;;;;;;AAUA,SAAgB,WAAW,MAAgB,SAAiB,KAA2B;CACrF,IAAI,OAAO,KAAK,gBAAA,UAAU,CAAC,SAAS,WAAW,OAAA,QAAO,MAAM,SAAS,IAAI,GAAG;EAE1E,KAAK,gBAAA,UAAU,CAAC,OAAO;EACvB,OAAO;CACT;CACA,OAAO;AACT;;;;;;;;AASA,SAAgB,eAAe,UAAoB,SAAiB,KAA4B;CAC9F,MAAM,OAAO,EAAE,GAAG,SAAS;CAC3B,IAAI,OAAO,KAAK,gBAAA,UAAU,CAAC,SAAS,WAAW,OAAA,QAAO,MAAM,SAAS,IAAI,GAEvE,KAAK,gBAAA,UAAU,CAAC,OAAO;CAEzB,OAAO;AACT;;;;;;;AAQA,SAAgB,eAAe,YAAY,YAAY;CACrD,MAAM,mBAAmB,OAAA,QAAO,QAAQ,YAAY,UAAU;CAE9D,OAAO,qBAAqB,KAAK,qBAAqB;AACxD;;;;;;AAOA,SAAgB,oBAAoB,SAA0C;CAC5E,MAAM,6BAAa,IAAI,IAAI;CAG3B,QAAQ,SAAS,SAAS;EACxB,MAAM,EAAE,MAAM,YAAY,KAAK;EAC/B,MAAM,MAAM;EAEZ,IAAI,WAAW,IAAI,IAAI,MAAM,SAAS,eAAe,SAAS,WAAW,IAAI,IAAI,CAAC,GAAG;GACnF,QAAM,iBAAiB,MAAM,OAAO;GACpC,WAAW,IAAI,KAAK,OAAO;EAC7B;CACF,CAAC;CAGD,OAAO,QAAQ,QAAQ,KAAK,SAAS;EACnC,MAAM,EAAE,MAAM,YAAY,KAAK;EAC/B,IACE,WAAW,IAAI,IAAI,KACnB,WAAW,IAAI,IAAI,MAAM,WACzB,IAAI,MAAM,MAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,KAAA,GAC7C;GACA,QAAM,gBAAgB,MAAM,OAAO;GACnC,IAAI,KAAK,IAAI;EACf;EACA,OAAO;CACT,GAAG,CAAC,CAAoC;AAC1C"}
1
+ {"version":3,"file":"versions-utils.js","names":[],"sources":["../../src/lib/versions-utils.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { isNil } from 'lodash-es';\nimport type { SemVer } from 'semver';\nimport semver from 'semver';\n\nimport type { searchUtils } from '@verdaccio/core';\nimport { DIST_TAGS, pkgUtils } from '@verdaccio/core';\nimport type { Manifest, StringValue, Version, Versions } from '@verdaccio/types';\n\nconst debug = buildDebug('verdaccio:storage:utils');\n\n/**\n * Gets version from a package object taking into account semver weirdness.\n * @return {String} return the semantic version of a package\n */\nexport function getVersion(versions: Versions, version: string): Version | undefined {\n if (!versions) {\n return;\n }\n\n // this condition must allow cast\n if (isNil(versions[version]) === false) {\n return versions[version];\n }\n\n const versionSemver: SemVer | null = semver.parse(version, true);\n if (versionSemver === null) {\n return;\n }\n\n for (const versionItem in versions) {\n if (Object.prototype.hasOwnProperty.call(versions, versionItem)) {\n // @ts-ignore\n if (versionSemver.compare(semver.parse(versionItem, true)) === 0) {\n return versions[versionItem];\n }\n }\n }\n}\n\n/**\n * Function filters out bad semver versions and sorts the array.\n * @return {Array} sorted Array\n */\nexport function sortVersionsAndFilterInvalid(listVersions: string[] /* logger */): string[] {\n return listVersions\n .filter(function (version): boolean {\n if (!semver.parse(version, true)) {\n return false;\n }\n return true;\n })\n .sort(semver.compareLoose)\n .map(String);\n}\n\n/**\n * Create a tag for a package\n * @param {*} data\n * @param {*} version\n * @param {*} tag\n * @return {Boolean} whether a package has been tagged\n * @deprecated\n */\nexport function tagVersion(data: Manifest, version: string, tag: StringValue): boolean {\n if (tag && data[DIST_TAGS][tag] !== version && semver.parse(version, true)) {\n // valid version - store\n data[DIST_TAGS][tag] = version;\n return true;\n }\n return false;\n}\n\n/**\n *\n * @param manifest\n * @param version\n * @param tag\n * @returns\n */\nexport function tagVersionNext(manifest: Manifest, version: string, tag: StringValue): Manifest {\n const data = { ...manifest };\n if (tag && data[DIST_TAGS][tag] !== version && semver.parse(version, true)) {\n // valid version - store\n data[DIST_TAGS][tag] = version;\n }\n return data;\n}\n\n/**\n * Whether `newVersion` is strictly newer than `oldVersion` under loose semver.\n * Equal versions in any spelling (`1.2.3`, `v1.2.3`, `01.2.3`, `1.2.3+build`) are\n * not newer, so search merges keep the entry they saw first, which is the local one.\n * Both arguments must already pass `pkgUtils.isValidVersion`.\n */\nexport function isNewerVersion(newVersion: string, oldVersion: string): boolean {\n return semver.compareLoose(newVersion, oldVersion) === 1;\n}\n\n/**\n * Remove duplicates from search results.\n * @param {Array} objects\n * @return {Array} filtered array\n */\nexport function removeLowerVersions(objects: searchUtils.SearchPackageItem[]) {\n const versionMap = new Map();\n const validObjects = objects.filter((item) => {\n const version = item?.package?.version;\n const valid = pkgUtils.isValidVersion(version);\n if (!valid) debug('ignoring invalid search version %o', version);\n return valid;\n });\n\n // Iterate through the array and keep the highest version for each name\n validObjects.forEach((item) => {\n const { name, version } = item.package;\n const key = name;\n\n if (versionMap.has(name) === false || isNewerVersion(version, versionMap.get(name))) {\n debug('keeping %o@%o', name, version);\n versionMap.set(key, version);\n }\n });\n\n // Filter objects based on the version map\n return validObjects.reduce((acc, item) => {\n const { name, version } = item.package;\n if (\n versionMap.has(name) &&\n versionMap.get(name) === version &&\n acc.find((i) => i.package.name === name) === undefined\n ) {\n debug('adding %o@%o', name, version);\n acc.push(item);\n }\n return acc;\n }, [] as searchUtils.SearchPackageItem[]);\n}\n"],"mappings":";;;;;;;;AASA,IAAM,WAAA,GAAQ,MAAA,QAAA,CAAW,yBAAyB;;;;;AAMlD,SAAgB,WAAW,UAAoB,SAAsC;CACnF,IAAI,CAAC,UACH;CAIF,KAAA,GAAI,UAAA,MAAA,CAAM,SAAS,QAAQ,MAAM,OAC/B,OAAO,SAAS;CAGlB,MAAM,gBAA+B,OAAA,QAAO,MAAM,SAAS,IAAI;CAC/D,IAAI,kBAAkB,MACpB;CAGF,KAAK,MAAM,eAAe,UACxB,IAAI,OAAO,UAAU,eAAe,KAAK,UAAU,WAAW,GAExD;MAAA,cAAc,QAAQ,OAAA,QAAO,MAAM,aAAa,IAAI,CAAC,MAAM,GAC7D,OAAO,SAAS;CAAA;AAIxB;;;;;AAMA,SAAgB,6BAA6B,cAA+C;CAC1F,OAAO,aACJ,OAAO,SAAU,SAAkB;EAClC,IAAI,CAAC,OAAA,QAAO,MAAM,SAAS,IAAI,GAC7B,OAAO;EAET,OAAO;CACT,CAAC,CAAC,CACD,KAAK,OAAA,QAAO,YAAY,CAAC,CACzB,IAAI,MAAM;AACf;;;;;;;;;AAUA,SAAgB,WAAW,MAAgB,SAAiB,KAA2B;CACrF,IAAI,OAAO,KAAK,gBAAA,UAAU,CAAC,SAAS,WAAW,OAAA,QAAO,MAAM,SAAS,IAAI,GAAG;EAE1E,KAAK,gBAAA,UAAU,CAAC,OAAO;EACvB,OAAO;CACT;CACA,OAAO;AACT;;;;;;;;AASA,SAAgB,eAAe,UAAoB,SAAiB,KAA4B;CAC9F,MAAM,OAAO,EAAE,GAAG,SAAS;CAC3B,IAAI,OAAO,KAAK,gBAAA,UAAU,CAAC,SAAS,WAAW,OAAA,QAAO,MAAM,SAAS,IAAI,GAEvE,KAAK,gBAAA,UAAU,CAAC,OAAO;CAEzB,OAAO;AACT;;;;;;;AAQA,SAAgB,eAAe,YAAoB,YAA6B;CAC9E,OAAO,OAAA,QAAO,aAAa,YAAY,UAAU,MAAM;AACzD;;;;;;AAOA,SAAgB,oBAAoB,SAA0C;CAC5E,MAAM,6BAAa,IAAI,IAAI;CAC3B,MAAM,eAAe,QAAQ,QAAQ,SAAS;EAC5C,MAAM,UAAU,MAAM,SAAS;EAC/B,MAAM,QAAQ,gBAAA,SAAS,eAAe,OAAO;EAC7C,IAAI,CAAC,OAAO,QAAM,sCAAsC,OAAO;EAC/D,OAAO;CACT,CAAC;CAGD,aAAa,SAAS,SAAS;EAC7B,MAAM,EAAE,MAAM,YAAY,KAAK;EAC/B,MAAM,MAAM;EAEZ,IAAI,WAAW,IAAI,IAAI,MAAM,SAAS,eAAe,SAAS,WAAW,IAAI,IAAI,CAAC,GAAG;GACnF,QAAM,iBAAiB,MAAM,OAAO;GACpC,WAAW,IAAI,KAAK,OAAO;EAC7B;CACF,CAAC;CAGD,OAAO,aAAa,QAAQ,KAAK,SAAS;EACxC,MAAM,EAAE,MAAM,YAAY,KAAK;EAC/B,IACE,WAAW,IAAI,IAAI,KACnB,WAAW,IAAI,IAAI,MAAM,WACzB,IAAI,MAAM,MAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,KAAA,GAC7C;GACA,QAAM,gBAAgB,MAAM,OAAO;GACnC,IAAI,KAAK,IAAI;EACf;EACA,OAAO;CACT,GAAG,CAAC,CAAoC;AAC1C"}
@@ -1,6 +1,6 @@
1
1
  import buildDebug from "debug";
2
2
  import { isNil } from "lodash-es";
3
- import { DIST_TAGS } from "@verdaccio/core";
3
+ import { DIST_TAGS, pkgUtils } from "@verdaccio/core";
4
4
  import semver from "semver";
5
5
  //#region src/lib/versions-utils.ts
6
6
  var debug = buildDebug("verdaccio:storage:utils");
@@ -55,14 +55,13 @@ function tagVersionNext(manifest, version, tag) {
55
55
  return data;
56
56
  }
57
57
  /**
58
- * Check if the version is newer than the older version.
59
- * @param newVersion
60
- * @param oldVersion
61
- * @returns
58
+ * Whether `newVersion` is strictly newer than `oldVersion` under loose semver.
59
+ * Equal versions in any spelling (`1.2.3`, `v1.2.3`, `01.2.3`, `1.2.3+build`) are
60
+ * not newer, so search merges keep the entry they saw first, which is the local one.
61
+ * Both arguments must already pass `pkgUtils.isValidVersion`.
62
62
  */
63
63
  function isNewerVersion(newVersion, oldVersion) {
64
- const comparisonResult = semver.compare(newVersion, oldVersion);
65
- return comparisonResult === 1 || comparisonResult === 0;
64
+ return semver.compareLoose(newVersion, oldVersion) === 1;
66
65
  }
67
66
  /**
68
67
  * Remove duplicates from search results.
@@ -71,7 +70,13 @@ function isNewerVersion(newVersion, oldVersion) {
71
70
  */
72
71
  function removeLowerVersions(objects) {
73
72
  const versionMap = /* @__PURE__ */ new Map();
74
- objects.forEach((item) => {
73
+ const validObjects = objects.filter((item) => {
74
+ const version = item?.package?.version;
75
+ const valid = pkgUtils.isValidVersion(version);
76
+ if (!valid) debug("ignoring invalid search version %o", version);
77
+ return valid;
78
+ });
79
+ validObjects.forEach((item) => {
75
80
  const { name, version } = item.package;
76
81
  const key = name;
77
82
  if (versionMap.has(name) === false || isNewerVersion(version, versionMap.get(name))) {
@@ -79,7 +84,7 @@ function removeLowerVersions(objects) {
79
84
  versionMap.set(key, version);
80
85
  }
81
86
  });
82
- return objects.reduce((acc, item) => {
87
+ return validObjects.reduce((acc, item) => {
83
88
  const { name, version } = item.package;
84
89
  if (versionMap.has(name) && versionMap.get(name) === version && acc.find((i) => i.package.name === name) === void 0) {
85
90
  debug("adding %o@%o", name, version);
@@ -1 +1 @@
1
- {"version":3,"file":"versions-utils.mjs","names":[],"sources":["../../src/lib/versions-utils.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { isNil } from 'lodash-es';\nimport type { SemVer } from 'semver';\nimport semver from 'semver';\n\nimport type { searchUtils } from '@verdaccio/core';\nimport { DIST_TAGS } from '@verdaccio/core';\nimport type { Manifest, StringValue, Version, Versions } from '@verdaccio/types';\n\nconst debug = buildDebug('verdaccio:storage:utils');\n\n/**\n * Gets version from a package object taking into account semver weirdness.\n * @return {String} return the semantic version of a package\n */\nexport function getVersion(versions: Versions, version: string): Version | undefined {\n if (!versions) {\n return;\n }\n\n // this condition must allow cast\n if (isNil(versions[version]) === false) {\n return versions[version];\n }\n\n const versionSemver: SemVer | null = semver.parse(version, true);\n if (versionSemver === null) {\n return;\n }\n\n for (const versionItem in versions) {\n if (Object.prototype.hasOwnProperty.call(versions, versionItem)) {\n // @ts-ignore\n if (versionSemver.compare(semver.parse(versionItem, true)) === 0) {\n return versions[versionItem];\n }\n }\n }\n}\n\n/**\n * Function filters out bad semver versions and sorts the array.\n * @return {Array} sorted Array\n */\nexport function sortVersionsAndFilterInvalid(listVersions: string[] /* logger */): string[] {\n return listVersions\n .filter(function (version): boolean {\n if (!semver.parse(version, true)) {\n return false;\n }\n return true;\n })\n .sort(semver.compareLoose)\n .map(String);\n}\n\n/**\n * Create a tag for a package\n * @param {*} data\n * @param {*} version\n * @param {*} tag\n * @return {Boolean} whether a package has been tagged\n * @deprecated\n */\nexport function tagVersion(data: Manifest, version: string, tag: StringValue): boolean {\n if (tag && data[DIST_TAGS][tag] !== version && semver.parse(version, true)) {\n // valid version - store\n data[DIST_TAGS][tag] = version;\n return true;\n }\n return false;\n}\n\n/**\n *\n * @param manifest\n * @param version\n * @param tag\n * @returns\n */\nexport function tagVersionNext(manifest: Manifest, version: string, tag: StringValue): Manifest {\n const data = { ...manifest };\n if (tag && data[DIST_TAGS][tag] !== version && semver.parse(version, true)) {\n // valid version - store\n data[DIST_TAGS][tag] = version;\n }\n return data;\n}\n\n/**\n * Check if the version is newer than the older version.\n * @param newVersion\n * @param oldVersion\n * @returns\n */\nexport function isNewerVersion(newVersion, oldVersion) {\n const comparisonResult = semver.compare(newVersion, oldVersion);\n\n return comparisonResult === 1 || comparisonResult === 0;\n}\n\n/**\n * Remove duplicates from search results.\n * @param {Array} objects\n * @return {Array} filtered array\n */\nexport function removeLowerVersions(objects: searchUtils.SearchPackageItem[]) {\n const versionMap = new Map();\n\n // Iterate through the array and keep the highest version for each name\n objects.forEach((item) => {\n const { name, version } = item.package;\n const key = name;\n\n if (versionMap.has(name) === false || isNewerVersion(version, versionMap.get(name))) {\n debug('keeping %o@%o', name, version);\n versionMap.set(key, version);\n }\n });\n\n // Filter objects based on the version map\n return objects.reduce((acc, item) => {\n const { name, version } = item.package;\n if (\n versionMap.has(name) &&\n versionMap.get(name) === version &&\n acc.find((i) => i.package.name === name) === undefined\n ) {\n debug('adding %o@%o', name, version);\n acc.push(item);\n }\n return acc;\n }, [] as searchUtils.SearchPackageItem[]);\n}\n"],"mappings":";;;;;AASA,IAAM,QAAQ,WAAW,yBAAyB;;;;;AAMlD,SAAgB,WAAW,UAAoB,SAAsC;CACnF,IAAI,CAAC,UACH;CAIF,IAAI,MAAM,SAAS,QAAQ,MAAM,OAC/B,OAAO,SAAS;CAGlB,MAAM,gBAA+B,OAAO,MAAM,SAAS,IAAI;CAC/D,IAAI,kBAAkB,MACpB;CAGF,KAAK,MAAM,eAAe,UACxB,IAAI,OAAO,UAAU,eAAe,KAAK,UAAU,WAAW,GAExD;MAAA,cAAc,QAAQ,OAAO,MAAM,aAAa,IAAI,CAAC,MAAM,GAC7D,OAAO,SAAS;CAAA;AAIxB;;;;;AAMA,SAAgB,6BAA6B,cAA+C;CAC1F,OAAO,aACJ,OAAO,SAAU,SAAkB;EAClC,IAAI,CAAC,OAAO,MAAM,SAAS,IAAI,GAC7B,OAAO;EAET,OAAO;CACT,CAAC,CAAC,CACD,KAAK,OAAO,YAAY,CAAC,CACzB,IAAI,MAAM;AACf;;;;;;;;;AAUA,SAAgB,WAAW,MAAgB,SAAiB,KAA2B;CACrF,IAAI,OAAO,KAAK,UAAU,CAAC,SAAS,WAAW,OAAO,MAAM,SAAS,IAAI,GAAG;EAE1E,KAAK,UAAU,CAAC,OAAO;EACvB,OAAO;CACT;CACA,OAAO;AACT;;;;;;;;AASA,SAAgB,eAAe,UAAoB,SAAiB,KAA4B;CAC9F,MAAM,OAAO,EAAE,GAAG,SAAS;CAC3B,IAAI,OAAO,KAAK,UAAU,CAAC,SAAS,WAAW,OAAO,MAAM,SAAS,IAAI,GAEvE,KAAK,UAAU,CAAC,OAAO;CAEzB,OAAO;AACT;;;;;;;AAQA,SAAgB,eAAe,YAAY,YAAY;CACrD,MAAM,mBAAmB,OAAO,QAAQ,YAAY,UAAU;CAE9D,OAAO,qBAAqB,KAAK,qBAAqB;AACxD;;;;;;AAOA,SAAgB,oBAAoB,SAA0C;CAC5E,MAAM,6BAAa,IAAI,IAAI;CAG3B,QAAQ,SAAS,SAAS;EACxB,MAAM,EAAE,MAAM,YAAY,KAAK;EAC/B,MAAM,MAAM;EAEZ,IAAI,WAAW,IAAI,IAAI,MAAM,SAAS,eAAe,SAAS,WAAW,IAAI,IAAI,CAAC,GAAG;GACnF,MAAM,iBAAiB,MAAM,OAAO;GACpC,WAAW,IAAI,KAAK,OAAO;EAC7B;CACF,CAAC;CAGD,OAAO,QAAQ,QAAQ,KAAK,SAAS;EACnC,MAAM,EAAE,MAAM,YAAY,KAAK;EAC/B,IACE,WAAW,IAAI,IAAI,KACnB,WAAW,IAAI,IAAI,MAAM,WACzB,IAAI,MAAM,MAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,KAAA,GAC7C;GACA,MAAM,gBAAgB,MAAM,OAAO;GACnC,IAAI,KAAK,IAAI;EACf;EACA,OAAO;CACT,GAAG,CAAC,CAAoC;AAC1C"}
1
+ {"version":3,"file":"versions-utils.mjs","names":[],"sources":["../../src/lib/versions-utils.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { isNil } from 'lodash-es';\nimport type { SemVer } from 'semver';\nimport semver from 'semver';\n\nimport type { searchUtils } from '@verdaccio/core';\nimport { DIST_TAGS, pkgUtils } from '@verdaccio/core';\nimport type { Manifest, StringValue, Version, Versions } from '@verdaccio/types';\n\nconst debug = buildDebug('verdaccio:storage:utils');\n\n/**\n * Gets version from a package object taking into account semver weirdness.\n * @return {String} return the semantic version of a package\n */\nexport function getVersion(versions: Versions, version: string): Version | undefined {\n if (!versions) {\n return;\n }\n\n // this condition must allow cast\n if (isNil(versions[version]) === false) {\n return versions[version];\n }\n\n const versionSemver: SemVer | null = semver.parse(version, true);\n if (versionSemver === null) {\n return;\n }\n\n for (const versionItem in versions) {\n if (Object.prototype.hasOwnProperty.call(versions, versionItem)) {\n // @ts-ignore\n if (versionSemver.compare(semver.parse(versionItem, true)) === 0) {\n return versions[versionItem];\n }\n }\n }\n}\n\n/**\n * Function filters out bad semver versions and sorts the array.\n * @return {Array} sorted Array\n */\nexport function sortVersionsAndFilterInvalid(listVersions: string[] /* logger */): string[] {\n return listVersions\n .filter(function (version): boolean {\n if (!semver.parse(version, true)) {\n return false;\n }\n return true;\n })\n .sort(semver.compareLoose)\n .map(String);\n}\n\n/**\n * Create a tag for a package\n * @param {*} data\n * @param {*} version\n * @param {*} tag\n * @return {Boolean} whether a package has been tagged\n * @deprecated\n */\nexport function tagVersion(data: Manifest, version: string, tag: StringValue): boolean {\n if (tag && data[DIST_TAGS][tag] !== version && semver.parse(version, true)) {\n // valid version - store\n data[DIST_TAGS][tag] = version;\n return true;\n }\n return false;\n}\n\n/**\n *\n * @param manifest\n * @param version\n * @param tag\n * @returns\n */\nexport function tagVersionNext(manifest: Manifest, version: string, tag: StringValue): Manifest {\n const data = { ...manifest };\n if (tag && data[DIST_TAGS][tag] !== version && semver.parse(version, true)) {\n // valid version - store\n data[DIST_TAGS][tag] = version;\n }\n return data;\n}\n\n/**\n * Whether `newVersion` is strictly newer than `oldVersion` under loose semver.\n * Equal versions in any spelling (`1.2.3`, `v1.2.3`, `01.2.3`, `1.2.3+build`) are\n * not newer, so search merges keep the entry they saw first, which is the local one.\n * Both arguments must already pass `pkgUtils.isValidVersion`.\n */\nexport function isNewerVersion(newVersion: string, oldVersion: string): boolean {\n return semver.compareLoose(newVersion, oldVersion) === 1;\n}\n\n/**\n * Remove duplicates from search results.\n * @param {Array} objects\n * @return {Array} filtered array\n */\nexport function removeLowerVersions(objects: searchUtils.SearchPackageItem[]) {\n const versionMap = new Map();\n const validObjects = objects.filter((item) => {\n const version = item?.package?.version;\n const valid = pkgUtils.isValidVersion(version);\n if (!valid) debug('ignoring invalid search version %o', version);\n return valid;\n });\n\n // Iterate through the array and keep the highest version for each name\n validObjects.forEach((item) => {\n const { name, version } = item.package;\n const key = name;\n\n if (versionMap.has(name) === false || isNewerVersion(version, versionMap.get(name))) {\n debug('keeping %o@%o', name, version);\n versionMap.set(key, version);\n }\n });\n\n // Filter objects based on the version map\n return validObjects.reduce((acc, item) => {\n const { name, version } = item.package;\n if (\n versionMap.has(name) &&\n versionMap.get(name) === version &&\n acc.find((i) => i.package.name === name) === undefined\n ) {\n debug('adding %o@%o', name, version);\n acc.push(item);\n }\n return acc;\n }, [] as searchUtils.SearchPackageItem[]);\n}\n"],"mappings":";;;;;AASA,IAAM,QAAQ,WAAW,yBAAyB;;;;;AAMlD,SAAgB,WAAW,UAAoB,SAAsC;CACnF,IAAI,CAAC,UACH;CAIF,IAAI,MAAM,SAAS,QAAQ,MAAM,OAC/B,OAAO,SAAS;CAGlB,MAAM,gBAA+B,OAAO,MAAM,SAAS,IAAI;CAC/D,IAAI,kBAAkB,MACpB;CAGF,KAAK,MAAM,eAAe,UACxB,IAAI,OAAO,UAAU,eAAe,KAAK,UAAU,WAAW,GAExD;MAAA,cAAc,QAAQ,OAAO,MAAM,aAAa,IAAI,CAAC,MAAM,GAC7D,OAAO,SAAS;CAAA;AAIxB;;;;;AAMA,SAAgB,6BAA6B,cAA+C;CAC1F,OAAO,aACJ,OAAO,SAAU,SAAkB;EAClC,IAAI,CAAC,OAAO,MAAM,SAAS,IAAI,GAC7B,OAAO;EAET,OAAO;CACT,CAAC,CAAC,CACD,KAAK,OAAO,YAAY,CAAC,CACzB,IAAI,MAAM;AACf;;;;;;;;;AAUA,SAAgB,WAAW,MAAgB,SAAiB,KAA2B;CACrF,IAAI,OAAO,KAAK,UAAU,CAAC,SAAS,WAAW,OAAO,MAAM,SAAS,IAAI,GAAG;EAE1E,KAAK,UAAU,CAAC,OAAO;EACvB,OAAO;CACT;CACA,OAAO;AACT;;;;;;;;AASA,SAAgB,eAAe,UAAoB,SAAiB,KAA4B;CAC9F,MAAM,OAAO,EAAE,GAAG,SAAS;CAC3B,IAAI,OAAO,KAAK,UAAU,CAAC,SAAS,WAAW,OAAO,MAAM,SAAS,IAAI,GAEvE,KAAK,UAAU,CAAC,OAAO;CAEzB,OAAO;AACT;;;;;;;AAQA,SAAgB,eAAe,YAAoB,YAA6B;CAC9E,OAAO,OAAO,aAAa,YAAY,UAAU,MAAM;AACzD;;;;;;AAOA,SAAgB,oBAAoB,SAA0C;CAC5E,MAAM,6BAAa,IAAI,IAAI;CAC3B,MAAM,eAAe,QAAQ,QAAQ,SAAS;EAC5C,MAAM,UAAU,MAAM,SAAS;EAC/B,MAAM,QAAQ,SAAS,eAAe,OAAO;EAC7C,IAAI,CAAC,OAAO,MAAM,sCAAsC,OAAO;EAC/D,OAAO;CACT,CAAC;CAGD,aAAa,SAAS,SAAS;EAC7B,MAAM,EAAE,MAAM,YAAY,KAAK;EAC/B,MAAM,MAAM;EAEZ,IAAI,WAAW,IAAI,IAAI,MAAM,SAAS,eAAe,SAAS,WAAW,IAAI,IAAI,CAAC,GAAG;GACnF,MAAM,iBAAiB,MAAM,OAAO;GACpC,WAAW,IAAI,KAAK,OAAO;EAC7B;CACF,CAAC;CAGD,OAAO,aAAa,QAAQ,KAAK,SAAS;EACxC,MAAM,EAAE,MAAM,YAAY,KAAK;EAC/B,IACE,WAAW,IAAI,IAAI,KACnB,WAAW,IAAI,IAAI,MAAM,WACzB,IAAI,MAAM,MAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,KAAA,GAC7C;GACA,MAAM,gBAAgB,MAAM,OAAO;GACnC,IAAI,KAAK,IAAI;EACf;EACA,OAAO;CACT,GAAG,CAAC,CAAoC;AAC1C"}
@@ -42,6 +42,8 @@ declare class Storage {
42
42
  * (privated and cached).
43
43
  */
44
44
  search(options: ProxySearchParams): Promise<searchUtils.SearchPackageItem[]>;
45
+ /** Cumulative, stable prefixes for Search v1; consumers stop once their page is full. */
46
+ searchPages(options: ProxySearchParams): AsyncGenerator<searchUtils.SearchPackageItem[], void, unknown>;
45
47
  private getTarballFromUpstream;
46
48
  private fetchTarballFromDistFile;
47
49
  /**
@@ -120,6 +122,16 @@ declare class Storage {
120
122
  * @param tags list of dist-tags
121
123
  */
122
124
  mergeTagsNext(name: string, tags: MergeTags): Promise<Manifest>;
125
+ /**
126
+ * Resolve the uplink to fetch a distfile url from, or null when the fetch
127
+ * must be refused.
128
+ *
129
+ * Off-uplink urls on a locally published package are not fetched: the
130
+ * client's `dist.tarball` is stored verbatim, and fetching it would be SSRF
131
+ * (and would attach uplink credentials when only one uplink is configured).
132
+ * Uplink-synced packages may still use an autogenerated proxy without
133
+ * credentials for CDN-hosted tarballs.
134
+ */
123
135
  private getUpLinkForDistFile;
124
136
  updateManifest(manifest: Manifest | OwnerManifestBody | UnPublishManifest, options: UpdateManifestOptions): Promise<string | undefined>;
125
137
  private deprecate;
package/build/storage.js CHANGED
@@ -159,39 +159,79 @@ var Storage = class {
159
159
  debug$1("unique results %o", uniqueResults.length);
160
160
  return uniqueResults;
161
161
  }
162
+ /** Cumulative, stable prefixes for Search v1; consumers stop once their page is full. */
163
+ async *searchPages(options) {
164
+ options.abort.signal.throwIfAborted();
165
+ const local = await this.getCachedPackages({
166
+ ...options.query,
167
+ from: 0,
168
+ size: _verdaccio_search.SEARCH_MAX_CANDIDATES
169
+ });
170
+ const merged = /* @__PURE__ */ new Map();
171
+ let candidates = 0;
172
+ const append = (items) => {
173
+ options.abort.signal.throwIfAborted();
174
+ candidates += items.length;
175
+ if (candidates > _verdaccio_search.SEARCH_MAX_CANDIDATES) throw _verdaccio_core.errorUtils.getServiceUnavailable("search pagination budget exhausted");
176
+ for (const item of items) {
177
+ if (!_verdaccio_core.pkgUtils.isValidVersion(item?.package?.version)) {
178
+ this.logger.warn({
179
+ name: item?.package?.name,
180
+ version: item?.package?.version
181
+ }, "ignoring invalid search version for @{name}: @{version}");
182
+ continue;
183
+ }
184
+ const previous = merged.get(item.package.name);
185
+ if (!previous || require_versions_utils.isNewerVersion(item.package.version, previous.package.version)) merged.set(item.package.name, item);
186
+ }
187
+ return [...merged.values()];
188
+ };
189
+ append(local);
190
+ let hadRound = false;
191
+ for await (const round of this.searchService.searchPages(options)) {
192
+ hadRound = true;
193
+ yield append(round);
194
+ }
195
+ if (!hadRound) yield [...merged.values()];
196
+ }
162
197
  async getTarballFromUpstream(name, filename, { signal }) {
163
198
  this.logger.info({
164
199
  name,
165
200
  filename
166
201
  }, "get tarball for package @{name} filename @{filename} from uplink");
167
202
  let cachedManifest = null;
203
+ let rawManifest = null;
168
204
  try {
169
- cachedManifest = await this.getPackageLocalMetadata(name);
170
- [cachedManifest] = await this.applyFilters(cachedManifest);
205
+ rawManifest = await this.getPackageLocalMetadata(name);
206
+ [cachedManifest] = await this.applyFilters(rawManifest);
171
207
  } catch (err) {
172
208
  debug$1("error on get package local metadata %o", err);
173
209
  }
174
210
  const cachedDistFile = cachedManifest !== null ? require_storage_utils.lookupDistFile(cachedManifest, filename) : null;
175
211
  if (cachedDistFile !== null && typeof cachedDistFile.url === "string") {
176
212
  debug$1("dist file found, using it %o", cachedDistFile.url);
177
- return this.fetchTarballFromDistFile(name, filename, cachedDistFile, { signal });
213
+ return this.fetchTarballFromDistFile(name, filename, cachedDistFile, cachedManifest, { signal });
178
214
  } else {
179
215
  debug$1("dist file not found, proceed update upstream");
180
- const [updatedManifest] = await this.syncUplinksMetadata(name, cachedManifest, { uplinksLook: true });
216
+ const [updatedManifest] = await this.syncUplinksMetadata(name, rawManifest, { uplinksLook: true });
181
217
  const distFile = updatedManifest === null ? null : require_storage_utils.lookupDistFile(updatedManifest, filename);
182
218
  if (updatedManifest === null || !distFile) {
183
219
  debug$1("remote tarball not found");
184
220
  throw _verdaccio_core.errorUtils.getNotFound(_verdaccio_core.API_ERROR.NO_SUCH_FILE);
185
221
  }
186
- return this.fetchTarballFromDistFile(name, filename, distFile, { signal });
222
+ return this.fetchTarballFromDistFile(name, filename, distFile, updatedManifest, { signal });
187
223
  }
188
224
  }
189
- fetchTarballFromDistFile(name, filename, distFile, { signal }) {
225
+ fetchTarballFromDistFile(name, filename, distFile, origin, { signal }) {
190
226
  let current_length = 0;
191
227
  let expected_length;
192
228
  let uplinkStatusError = null;
193
229
  const passThroughRemoteStream = new node_stream.PassThrough();
194
- const proxy = this.getUpLinkForDistFile(name, distFile);
230
+ const proxy = this.getUpLinkForDistFile(name, filename, distFile, origin);
231
+ if (proxy === null) {
232
+ setImmediate(() => passThroughRemoteStream.emit("error", _verdaccio_core.errorUtils.getNotFound(_verdaccio_core.API_ERROR.NO_SUCH_FILE)));
233
+ return passThroughRemoteStream;
234
+ }
195
235
  const remoteStream = proxy.fetchTarball(distFile.url, { throwHttpErrors: false });
196
236
  const readRemoteBody = () => new Promise((resolve) => {
197
237
  const chunks = [];
@@ -296,7 +336,14 @@ var Storage = class {
296
336
  const localStream = await this.getLocalTarball(name, filename, { signal });
297
337
  forwardContentLength(localStream);
298
338
  localStream.on("open", async () => {
299
- await (0, node_stream_promises.pipeline)(localStream, localTarballStream, { signal });
339
+ try {
340
+ await (0, node_stream_promises.pipeline)(localStream, localTarballStream, { signal });
341
+ } catch (err) {
342
+ this.logger.warn({
343
+ err,
344
+ filename
345
+ }, "error streaming local tarball @{filename}: @{err.message}");
346
+ }
300
347
  });
301
348
  localStream.on("error", (err) => {
302
349
  if (err.code === require_storage_utils.STORAGE.NO_SUCH_FILE_ERROR || err.code === _verdaccio_core.HTTP_STATUS.NOT_FOUND) this.getTarballFromUpstream(name, filename, { signal }).then((uplinkStream) => {
@@ -628,18 +675,45 @@ var Storage = class {
628
675
  return newData;
629
676
  });
630
677
  }
631
- getUpLinkForDistFile(pkgName, distFile) {
632
- if (distFile.registry && this.uplinks[distFile.registry]) {
633
- debug$1("tarball is served by the recorded uplink %o", distFile.registry);
634
- return this.uplinks[distFile.registry];
635
- }
678
+ /**
679
+ * Resolve the uplink to fetch a distfile url from, or null when the fetch
680
+ * must be refused.
681
+ *
682
+ * Off-uplink urls on a locally published package are not fetched: the
683
+ * client's `dist.tarball` is stored verbatim, and fetching it would be SSRF
684
+ * (and would attach uplink credentials when only one uplink is configured).
685
+ * Uplink-synced packages may still use an autogenerated proxy without
686
+ * credentials for CDN-hosted tarballs.
687
+ */
688
+ getUpLinkForDistFile(pkgName, filename, distFile, origin) {
636
689
  const candidates = [];
637
690
  for (const uplinkName in this.uplinks) if ((0, _verdaccio_config.hasProxyTo)(pkgName, uplinkName, this.config.packages)) candidates.push(this.uplinks[uplinkName]);
638
691
  let uplink = null;
639
- for (const candidate of candidates) if (require_storage_utils.uplinkServesUrl(candidate.url, distFile.url)) uplink = candidate;
640
- if (uplink === null && candidates.length === 1) uplink = candidates[0];
692
+ if (distFile.registry && this.uplinks[distFile.registry]) {
693
+ const recorded = this.uplinks[distFile.registry];
694
+ if (require_storage_utils.uplinkServesUrl(recorded.url, distFile.url)) {
695
+ debug$1("tarball is served by the recorded uplink %o", distFile.registry);
696
+ uplink = recorded;
697
+ }
698
+ }
699
+ if (uplink === null) {
700
+ for (const candidate of candidates) if (require_storage_utils.uplinkServesUrl(candidate.url, distFile.url)) uplink = candidate;
701
+ }
641
702
  if (uplink === null) {
642
- debug$1("upstream not found, creating one for %o", pkgName);
703
+ if (!Boolean(origin?._distfiles?.[filename])) {
704
+ debug$1("refusing off-uplink tarball fetch for locally published %o (%o)", pkgName, distFile.url);
705
+ this.logger.warn({
706
+ name: pkgName,
707
+ filename,
708
+ url: distFile.url
709
+ }, "refused off-uplink tarball fetch for @{name} (@{url})");
710
+ return null;
711
+ }
712
+ debug$1("upstream not found, creating one for %o without uplink credentials", pkgName);
713
+ this.logger.http({
714
+ name: pkgName,
715
+ url: distFile.url
716
+ }, "serving off-uplink tarball via autogenerated proxy for @{name} (@{url})");
643
717
  uplink = new _verdaccio_proxy.ProxyStorage(`verdaccio-${pkgName}`, {
644
718
  url: distFile.url,
645
719
  cache: true
@@ -887,10 +961,24 @@ var Storage = class {
887
961
  });
888
962
  else {
889
963
  const localStorageWriteStream = await storage.writeTarball(filename, { signal });
964
+ let uploadFailed = false;
890
965
  localStorageWriteStream.on("open", async () => {
891
- await (0, node_stream_promises.pipeline)(uploadStream, transformHash, localStorageWriteStream, { signal });
966
+ try {
967
+ await (0, node_stream_promises.pipeline)(uploadStream, transformHash, localStorageWriteStream, { signal });
968
+ } catch (err) {
969
+ uploadFailed = true;
970
+ this.logger.warn({
971
+ err,
972
+ filename,
973
+ pkgName
974
+ }, "error uploading tarball @{filename} for @{pkgName}: @{err.message}");
975
+ }
892
976
  });
893
977
  localStorageWriteStream.on("close", async () => {
978
+ if (uploadFailed) {
979
+ debug$1("skip metadata update for failed upload %o for %o", filename, pkgName);
980
+ return;
981
+ }
894
982
  try {
895
983
  debug$1("uploaded tarball %o for %o", filename, pkgName);
896
984
  await this.updatePackage(pkgName, async (data) => {
@@ -907,6 +995,7 @@ var Storage = class {
907
995
  }
908
996
  });
909
997
  localStorageWriteStream.on("error", async (err) => {
998
+ uploadFailed = true;
910
999
  uploadStream.emit("error", err);
911
1000
  });
912
1001
  }
@@ -1152,6 +1241,15 @@ var Storage = class {
1152
1241
  break;
1153
1242
  }
1154
1243
  } catch (err) {
1244
+ const uplinkName = uplink;
1245
+ const upLinkMeta = localManifest?._uplinks?.[uplinkName];
1246
+ const etag = upLinkMeta?.etag;
1247
+ if (err.code === _verdaccio_core.HTTP_STATUS.NOT_MODIFIED && localManifest !== null && _verdaccio_core.validationUtils.isObject(upLinkMeta) && typeof etag === "string") {
1248
+ syncManifest = require_storage_utils.updateUpLinkMetadata(uplinkName, localManifest, etag);
1249
+ localManifest = syncManifest;
1250
+ found = true;
1251
+ continue;
1252
+ }
1155
1253
  debug$1("error captured on uplink %o", err.message);
1156
1254
  uplinksErrors.push(err);
1157
1255
  continue;