@verdaccio/store 7.0.0-next.5 → 8.0.0-next-8.0

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +359 -0
  2. package/LICENSE +1 -1
  3. package/README.md +24 -57
  4. package/build/index.d.ts +0 -1
  5. package/build/index.js +0 -12
  6. package/build/index.js.map +1 -1
  7. package/build/lib/star-utils.js.map +1 -1
  8. package/build/lib/storage-utils.d.ts +3 -0
  9. package/build/lib/storage-utils.js +35 -2
  10. package/build/lib/storage-utils.js.map +1 -1
  11. package/build/lib/versions-utils.d.ts +14 -0
  12. package/build/lib/versions-utils.js +52 -1
  13. package/build/lib/versions-utils.js.map +1 -1
  14. package/build/local-storage.js +3 -3
  15. package/build/local-storage.js.map +1 -1
  16. package/build/storage.d.ts +14 -15
  17. package/build/storage.js +199 -162
  18. package/build/storage.js.map +1 -1
  19. package/build/type.d.ts +5 -0
  20. package/build/type.js.map +1 -1
  21. package/jest.config.js +2 -2
  22. package/package.json +18 -17
  23. package/src/index.ts +0 -1
  24. package/src/lib/storage-utils.ts +40 -2
  25. package/src/lib/versions-utils.ts +50 -1
  26. package/src/local-storage.ts +3 -2
  27. package/src/storage.ts +230 -150
  28. package/src/type.ts +6 -0
  29. package/test/fixtures/config/publishWithOwnerAndCheck.yaml +18 -0
  30. package/test/fixtures/config/publishWithOwnerDefault.yaml +15 -0
  31. package/test/fixtures/config/syncDoubleUplinksMetadata.yaml +2 -18
  32. package/test/search.spec.ts +28 -7
  33. package/test/storage-utils.spec.ts +1 -0
  34. package/test/storage.spec.ts +466 -80
  35. package/test/versions.spec.ts +58 -1
  36. package/build/lib/TransFormResults.d.ts +0 -15
  37. package/build/lib/TransFormResults.js +0 -51
  38. package/build/lib/TransFormResults.js.map +0 -1
  39. package/build/lib/search-utils.d.ts +0 -4
  40. package/build/lib/search-utils.js +0 -50
  41. package/build/lib/search-utils.js.map +0 -1
  42. package/build/lib/uplink-util.d.ts +0 -10
  43. package/build/lib/uplink-util.js +0 -43
  44. package/build/lib/uplink-util.js.map +0 -1
  45. package/src/lib/TransFormResults.ts +0 -42
  46. package/src/lib/search-utils.ts +0 -50
  47. package/src/lib/uplink-util.ts +0 -42
  48. /package/test/fixtures/config/{getTarballNext-getupstream.yaml → getTarball-getupstream.yaml} +0 -0
package/build/storage.js CHANGED
@@ -14,32 +14,30 @@ var _url = _interopRequireDefault(require("url"));
14
14
  var _config = require("@verdaccio/config");
15
15
  var _core = require("@verdaccio/core");
16
16
  var _loaders = require("@verdaccio/loaders");
17
- var _logger = require("@verdaccio/logger");
18
17
  var _proxy = require("@verdaccio/proxy");
18
+ var _search = _interopRequireDefault(require("@verdaccio/search"));
19
19
  var _tarball = require("@verdaccio/tarball");
20
20
  var _utils = require("@verdaccio/utils");
21
21
  var _2 = require(".");
22
- var _TransFormResults = require("./lib/TransFormResults");
23
- var _searchUtils = require("./lib/search-utils");
24
22
  var _starUtils = require("./lib/star-utils");
25
23
  var _storageUtils = require("./lib/storage-utils");
26
- var _uplinkUtil = require("./lib/uplink-util");
27
24
  var _versionsUtils = require("./lib/versions-utils");
28
25
  var _localStorage = require("./local-storage");
29
26
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
30
- function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
31
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
27
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
28
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
32
29
  const debug = (0, _debug.default)('verdaccio:storage');
33
30
  const noSuchFile = exports.noSuchFile = 'ENOENT';
34
31
  const resourceNotAvailable = exports.resourceNotAvailable = 'EAGAIN';
35
32
  const PROTO_NAME = exports.PROTO_NAME = '__proto__';
36
33
  class Storage {
37
- constructor(config) {
34
+ constructor(config, logger) {
38
35
  this.config = config;
39
- this.uplinks = (0, _uplinkUtil.setupUpLinks)(config);
40
- this.logger = _logger.logger.child({
36
+ this.logger = logger.child({
41
37
  module: 'storage'
42
38
  });
39
+ this.uplinks = (0, _proxy.setupUpLinks)(config, this.logger);
40
+ this.searchService = new _search.default(config, this.logger);
43
41
  this.filters = null;
44
42
  // @ts-ignore
45
43
  this.localStorage = null;
@@ -54,12 +52,12 @@ class Storage {
54
52
  */
55
53
  async changePackage(name, metadata, revision) {
56
54
  debug('change existing package for package %o revision %o', name, revision);
57
- debug(`change manifest tags for %o revision %s`, name, revision);
55
+ debug(`change manifest tags for %o revision %o`, name, revision);
58
56
  if (!_core.validatioUtils.isObject(metadata.versions) || !_core.validatioUtils.isObject(metadata[_core.DIST_TAGS])) {
59
57
  debug(`change manifest bad data for %o`, name);
60
58
  throw _core.errorUtils.getBadData();
61
59
  }
62
- debug(`change manifest udapting manifest for %o`, name);
60
+ debug(`change manifest updating manifest for %o`, name);
63
61
  await this.updatePackage(name, async localData => {
64
62
  // eslint-disable-next-line guard-for-in
65
63
  for (const version in localData.versions) {
@@ -100,12 +98,13 @@ class Storage {
100
98
  }
101
99
  localData[_core.USERS] = metadata[_core.USERS];
102
100
  localData[_core.DIST_TAGS] = metadata[_core.DIST_TAGS];
101
+ localData[_core.MAINTAINERS] = metadata[_core.MAINTAINERS];
103
102
  return localData;
104
103
  });
105
104
  }
106
- async removePackage(name, revision) {
105
+ async removePackage(name, revision, username) {
107
106
  debug('remove package %o', name);
108
- await this.removePackageByRevision(name, revision);
107
+ await this.removePackageByRevision(name, revision, username);
109
108
  }
110
109
 
111
110
  /**
@@ -115,8 +114,9 @@ class Storage {
115
114
  versions, i.e. package version should be unpublished first.
116
115
  Used storage: local (write)
117
116
  */
117
+ async removeTarball(name, filename,
118
118
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
119
- async removeTarball(name, filename, _revision) {
119
+ _revision, username) {
120
120
  debug('remove tarball %s for %s', filename, name);
121
121
  (0, _assert.default)(_core.validatioUtils.validateName(filename));
122
122
  const storage = this.getPrivatePackageStorage(name);
@@ -130,6 +130,9 @@ class Storage {
130
130
  if (!cacheManifest._attachments[filename]) {
131
131
  throw _core.errorUtils.getNotFound('no such file available');
132
132
  }
133
+
134
+ // check if logged in user is allowed to remove tarball
135
+ await this.checkAllowedToChangePackage(cacheManifest, username);
133
136
  } catch (err) {
134
137
  if (err.code === noSuchFile) {
135
138
  throw _core.errorUtils.getNotFound();
@@ -164,79 +167,24 @@ class Storage {
164
167
  /**
165
168
  * Handle search on packages and proxies.
166
169
  * Iterate all proxies configured and search in all endpoints in v2 and pipe all responses
167
- * to a stream, once the proxies request has finished search in local storage for all packages
170
+ * once the proxies request has finished search in local storage for all packages
168
171
  * (privated and cached).
169
172
  */
170
173
  async search(options) {
171
- const transformResults = new _TransFormResults.TransFormResults({
172
- objectMode: true
173
- });
174
- const streamPassThrough = new _stream.PassThrough({
175
- objectMode: true
176
- });
177
- const upLinkList = this.getProxyList();
178
- debug('uplinks found %s', upLinkList.length);
179
- const searchUplinksStreams = upLinkList.map(uplinkId => {
180
- const uplink = this.uplinks[uplinkId];
181
- if (!uplink) {
182
- // this line should never happens
183
- this.logger.error({
184
- uplinkId
185
- }, 'uplink @upLinkId not found');
186
- }
187
- return this.consumeSearchStream(uplinkId, uplink, options, streamPassThrough);
188
- });
189
- try {
190
- debug('searching on %s uplinks...', searchUplinksStreams === null || searchUplinksStreams === void 0 ? void 0 : searchUplinksStreams.length);
191
- // only process those streams end successfully, if all request fails
192
- // just include local storage results (if local fails then return 500)
193
- await Promise.allSettled([...searchUplinksStreams]);
194
- debug('searching all uplinks done');
195
- } catch (err) {
196
- this.logger.error({
197
- err: err === null || err === void 0 ? void 0 : err.message
198
- }, ' error on uplinks search @{err}');
199
- streamPassThrough.emit('error', err);
200
- }
201
- debug('search local');
202
- try {
203
- await this.searchCachedPackages(streamPassThrough, options.query);
204
- } catch (err) {
205
- this.logger.error({
206
- err: err === null || err === void 0 ? void 0 : err.message
207
- }, ' error on local search @{err}');
208
- streamPassThrough.emit('error', err);
209
- }
210
- const data = [];
211
- const outPutStream = new _stream.PassThrough({
212
- objectMode: true
213
- });
214
- (0, _stream.pipeline)(streamPassThrough, transformResults, outPutStream, err => {
215
- if (err) {
216
- this.logger.error({
217
- err: err === null || err === void 0 ? void 0 : err.message
218
- }, ' error on search @{err}');
219
- throw _core.errorUtils.getInternalError(err ? err.message : 'unknown search error');
220
- } else {
221
- debug('pipeline succeeded');
222
- }
223
- });
224
- outPutStream.on('data', chunk => {
225
- data.push(chunk);
226
- });
227
- return new Promise(resolve => {
228
- outPutStream.on('finish', async () => {
229
- const searchFinalResults = (0, _searchUtils.removeDuplicates)(data);
230
- debug('search stream total results: %o', searchFinalResults.length);
231
- return resolve(searchFinalResults);
232
- });
233
- debug('search done');
234
- });
174
+ debug('search on cache packages');
175
+ const cachePackages = await this.getCachedPackages(options.query);
176
+ debug('search found on cache packages %o', cachePackages.length);
177
+ const remotePackages = await this.searchService.search(options);
178
+ debug('search found on remote packages %o', remotePackages.length);
179
+ const totalResults = [...cachePackages, ...remotePackages];
180
+ const uniqueResults = (0, _versionsUtils.removeLowerVersions)(totalResults);
181
+ debug('unique results %o', uniqueResults.length);
182
+ return uniqueResults;
235
183
  }
236
184
  async getTarballFromUpstream(name, filename, {
237
185
  signal
238
186
  }) {
239
- var _cachedManifest, _cachedManifest2, _cachedManifest2$_dis;
187
+ var _cachedManifest, _cachedManifest2;
240
188
  let cachedManifest = null;
241
189
  try {
242
190
  cachedManifest = await this.getPackageLocalMetadata(name);
@@ -244,7 +192,7 @@ class Storage {
244
192
  debug('error on get package local metadata %o', err);
245
193
  }
246
194
  // dist url should be on local cache metadata
247
- if ((_cachedManifest = cachedManifest) !== null && _cachedManifest !== void 0 && _cachedManifest._distfiles && typeof ((_cachedManifest2 = cachedManifest) === null || _cachedManifest2 === void 0 ? void 0 : (_cachedManifest2$_dis = _cachedManifest2._distfiles[filename]) === null || _cachedManifest2$_dis === void 0 ? void 0 : _cachedManifest2$_dis.url) === 'string') {
195
+ if ((_cachedManifest = cachedManifest) !== null && _cachedManifest !== void 0 && _cachedManifest._distfiles && typeof ((_cachedManifest2 = cachedManifest) === null || _cachedManifest2 === void 0 || (_cachedManifest2 = _cachedManifest2._distfiles[filename]) === null || _cachedManifest2 === void 0 ? void 0 : _cachedManifest2.url) === 'string') {
248
196
  var _cachedManifest3;
249
197
  debug('dist file found, using it %o', (_cachedManifest3 = cachedManifest) === null || _cachedManifest3 === void 0 ? void 0 : _cachedManifest3._distfiles[filename].url);
250
198
  // dist file found, proceed to download
@@ -313,7 +261,7 @@ class Storage {
313
261
  // should not be the case
314
262
  const passThroughRemoteStream = new _stream.PassThrough();
315
263
  // ensure get the latest data
316
- const [updatedManifest] = await this.syncUplinksMetadataNext(name, cachedManifest, {
264
+ const [updatedManifest] = await this.syncUplinksMetadata(name, cachedManifest, {
317
265
  uplinksLook: true
318
266
  });
319
267
  const distFile = updatedManifest._distfiles[filename];
@@ -356,7 +304,7 @@ class Storage {
356
304
  * @param param2
357
305
  * @returns
358
306
  */
359
- async getTarballNext(name, filename, {
307
+ async getTarball(name, filename, {
360
308
  signal
361
309
  }) {
362
310
  debug('get tarball for package %o filename %o', name, filename);
@@ -438,6 +386,18 @@ class Storage {
438
386
  async getPackageManifest(options) {
439
387
  // convert dist remotes to local bars
440
388
  const [manifest] = await this.getPackageNext(options);
389
+
390
+ // If change access is requested (?write=true), then check if logged in user is allowed to change package
391
+ if (options.byPassCache === true) {
392
+ try {
393
+ await this.checkAllowedToChangePackage(manifest, options.requestOptions.username);
394
+ } catch (error) {
395
+ this.logger.error({
396
+ error: error.message
397
+ }, 'getting package has failed: @{error}');
398
+ throw _core.errorUtils.getBadRequest(error.message);
399
+ }
400
+ }
441
401
  const convertedManifest = (0, _tarball.convertDistRemoteToLocalTarballUrls)(manifest, options.requestOptions, this.config.url_prefix);
442
402
  return convertedManifest;
443
403
  }
@@ -449,7 +409,6 @@ class Storage {
449
409
  const _version_abbreviated = {
450
410
  name: _version.name,
451
411
  version: _version.version,
452
- description: _version.description,
453
412
  deprecated: _version.deprecated,
454
413
  bin: _version.bin,
455
414
  dist: _version.dist,
@@ -461,6 +420,10 @@ class Storage {
461
420
  peerDependencies: _version.peerDependencies,
462
421
  optionalDependencies: _version.optionalDependencies,
463
422
  bundleDependencies: _version.bundleDependencies,
423
+ cpu: _version.cpu,
424
+ os: _version.os,
425
+ peerDependenciesMeta: _version.peerDependenciesMeta,
426
+ acceptDependencies: _version.acceptDependencies,
464
427
  // npm cli specifics
465
428
  _hasShrinkwrap: _version._hasShrinkwrap,
466
429
  hasInstallScript: _version.hasInstallScript
@@ -552,7 +515,7 @@ class Storage {
552
515
  */
553
516
  async init(config) {
554
517
  if (this.localStorage === null) {
555
- this.localStorage = new _localStorage.LocalStorage(this.config, _logger.logger);
518
+ this.localStorage = new _localStorage.LocalStorage(this.config, this.logger);
556
519
  await this.localStorage.init();
557
520
  debug('local init storage initialized');
558
521
  await this.localStorage.getSecret(config);
@@ -561,39 +524,18 @@ class Storage {
561
524
  debug('storage has been already initialized');
562
525
  }
563
526
  if (!this.filters) {
564
- var _this$config, _this$config$serverSe;
527
+ var _this$config;
565
528
  this.filters = await (0, _loaders.asyncLoadPlugin)(this.config.filters, {
566
529
  config: this.config,
567
530
  logger: this.logger
568
531
  }, plugin => {
569
532
  return typeof plugin.filter_metadata !== 'undefined';
570
- }, (_this$config = this.config) === null || _this$config === void 0 ? void 0 : (_this$config$serverSe = _this$config.serverSettings) === null || _this$config$serverSe === void 0 ? void 0 : _this$config$serverSe.pluginPrefix);
571
- debug('filters available %o', this.filters);
533
+ }, (_this$config = this.config) === null || _this$config === void 0 || (_this$config = _this$config.serverSettings) === null || _this$config === void 0 ? void 0 : _this$config.pluginPrefix, _core.PLUGIN_CATEGORY.FILTER);
534
+ debug('filters available %o', this.filters.length);
572
535
  }
573
536
  return;
574
537
  }
575
538
 
576
- /**
577
- * Consume the upstream and pipe it to a transformable stream.
578
- */
579
- consumeSearchStream(uplinkId, uplink, options, searchPassThrough) {
580
- return uplink.search({
581
- ...options
582
- }).then(bodyStream => {
583
- bodyStream.pipe(searchPassThrough, {
584
- end: false
585
- });
586
- bodyStream.on('error', err => {
587
- _logger.logger.error({
588
- uplinkId,
589
- err: err
590
- }, 'search error for uplink @{uplinkId}: @{err?.message}');
591
- searchPassThrough.end();
592
- });
593
- return new Promise(resolve => bodyStream.on('end', resolve));
594
- });
595
- }
596
-
597
539
  /**
598
540
  * Retrieve a wrapper that provide access to the package location.
599
541
  * @param {Object} pkgName package name.
@@ -623,8 +565,13 @@ class Storage {
623
565
  signal
624
566
  });
625
567
  }
626
- async searchCachedPackages(searchStream, query) {
627
- debug('search on each package');
568
+ async getCachedPackages(query) {
569
+ debug('search on each package', query);
570
+ const results = [];
571
+ if (typeof query === 'undefined' || typeof (query === null || query === void 0 ? void 0 : query.text) === 'undefined') {
572
+ debug('search query for cached not found');
573
+ return results;
574
+ }
628
575
  this.logger.info({
629
576
  t: query.text,
630
577
  q: query.quality,
@@ -634,15 +581,15 @@ class Storage {
634
581
  }, 'search by text @{t}| maintenance @{m}| quality @{q}| popularity @{p}');
635
582
  if (typeof this.localStorage.getStoragePlugin().search === 'undefined') {
636
583
  this.logger.info('plugin search not implemented yet');
637
- searchStream.end();
638
584
  } else {
639
- debug('search on each package by plugin');
585
+ debug('search on each package by plugin query');
640
586
  const items = await this.localStorage.getStoragePlugin().search(query);
641
587
  try {
642
588
  for (const searchItem of items) {
643
589
  const manifest = await this.getPackageLocalMetadata(searchItem.package.name);
644
590
  if (_lodash.default.isEmpty(manifest === null || manifest === void 0 ? void 0 : manifest.versions) === false) {
645
- const searchPackage = (0, _2.mapManifestToSearchPackageBody)(manifest, searchItem);
591
+ const searchPackage = (0, _storageUtils.mapManifestToSearchPackageBody)(manifest, searchItem);
592
+ debug('search local stream found %o', searchPackage.name);
646
593
  const searchPackageItem = {
647
594
  package: searchPackage,
648
595
  score: searchItem.score,
@@ -652,21 +599,23 @@ class Storage {
652
599
  // FUTURE: find a better way to calculate the score
653
600
  searchScore: 1
654
601
  };
655
- searchStream.write(searchPackageItem);
602
+ results.push(searchPackageItem);
603
+ } else {
604
+ debug('local item without versions detected %s', searchItem.package.name);
656
605
  }
657
606
  }
658
607
  debug('search local stream end');
659
- searchStream.end();
660
608
  } catch (err) {
661
609
  this.logger.error({
662
610
  err,
663
611
  query
664
612
  }, 'error on search by plugin @{err.message}');
665
- searchStream.emit('error', err);
613
+ throw err;
666
614
  }
667
615
  }
616
+ return results;
668
617
  }
669
- async removePackageByRevision(pkgName, revision) {
618
+ async removePackageByRevision(pkgName, revision, username) {
670
619
  const storage = this.getPrivatePackageStorage(pkgName);
671
620
  debug('get package metadata for %o', pkgName);
672
621
  if (typeof storage === 'undefined') {
@@ -678,13 +627,13 @@ class Storage {
678
627
  manifest = (0, _storageUtils.normalizePackage)(manifest);
679
628
  } catch (err) {
680
629
  if (err.code === _storageUtils.STORAGE.NO_SUCH_FILE_ERROR || err.code === _core.HTTP_STATUS.NOT_FOUND) {
681
- _logger.logger.info({
630
+ this.logger.info({
682
631
  pkgName,
683
632
  revision
684
633
  }, 'package not found');
685
634
  throw _core.errorUtils.getNotFound();
686
635
  }
687
- _logger.logger.error({
636
+ this.logger.error({
688
637
  pkgName,
689
638
  revision,
690
639
  err: err.message
@@ -694,6 +643,8 @@ class Storage {
694
643
 
695
644
  // TODO: move this to another method
696
645
  try {
646
+ // check if logged in user is allowed to remove package
647
+ await this.checkAllowedToChangePackage(manifest, username);
697
648
  await this.localStorage.getStoragePlugin().remove(pkgName);
698
649
  // remove each attachment
699
650
  const attachments = Object.keys(manifest._attachments);
@@ -701,7 +652,7 @@ class Storage {
701
652
  for (let attachment of attachments) {
702
653
  debug('remove attachment %s', attachment);
703
654
  await storage.deletePackage(attachment);
704
- _logger.logger.info({
655
+ this.logger.info({
705
656
  attachment
706
657
  }, 'attachment @{attachment} removed');
707
658
  }
@@ -711,7 +662,7 @@ class Storage {
711
662
  // remove folder
712
663
  debug('remove package folder');
713
664
  await storage.removePackage();
714
- _logger.logger.info({
665
+ this.logger.info({
715
666
  pkgName
716
667
  }, 'package @{pkgName} removed');
717
668
  } catch (err) {
@@ -805,11 +756,12 @@ class Storage {
805
756
  uplink = new _proxy.ProxyStorage({
806
757
  url: distFile.url,
807
758
  cache: true
808
- }, this.config, _logger.logger);
759
+ }, this.config, this.logger);
809
760
  }
810
761
  return uplink;
811
762
  }
812
763
  async updateManifest(manifest, options) {
764
+ debug('update manifest %o for user %o', manifest._id, options.requestOptions.username);
813
765
  if ((0, _2.isDeprecatedManifest)(manifest)) {
814
766
  // if the manifest is deprecated, we need to update the package.json
815
767
  await this.deprecate(manifest, {
@@ -821,6 +773,12 @@ class Storage {
821
773
  ...options
822
774
  });
823
775
  return _core.API_MESSAGE.PKG_CHANGED;
776
+ } else if ((0, _starUtils.isPublishablePackage)(manifest) === false && Array.isArray(manifest.maintainers)) {
777
+ // if user request to change owners of package
778
+ await this.changeOwners(manifest, {
779
+ ...options
780
+ });
781
+ return _core.API_MESSAGE.PKG_CHANGED;
824
782
  } else if (_core.validatioUtils.validatePublishSingleVersion(manifest)) {
825
783
  // if continue, the version to be published does not exist
826
784
  // we create a new package
@@ -833,19 +791,19 @@ class Storage {
833
791
  name
834
792
  } = mergedManifest;
835
793
  await this.notify(mergedManifest, `${name}@${version}`);
836
- _logger.logger.info({
794
+ this.logger.info({
837
795
  name,
838
796
  version
839
797
  }, 'notify for @{name}@@{version} has been sent');
840
798
  } catch (error) {
841
- _logger.logger.error({
799
+ this.logger.error({
842
800
  error: error.message
843
801
  }, 'notify batch service has failed: @{error}');
844
802
  }
845
803
  return message;
846
804
  } else {
847
805
  debug('invalid body format');
848
- _logger.logger.info({
806
+ this.logger.warn({
849
807
  packageName: options.name
850
808
  }, `wrong package format on publish a package @{packageName}`);
851
809
  throw _core.errorUtils.getBadRequest(_core.API_ERROR.UNSUPORTED_REGISTRY_CALL);
@@ -900,6 +858,35 @@ class Storage {
900
858
  }, options.revision);
901
859
  return _core.API_MESSAGE.PKG_CHANGED;
902
860
  }
861
+ async changeOwners(manifest, options) {
862
+ const {
863
+ maintainers
864
+ } = manifest;
865
+ const {
866
+ requestOptions,
867
+ name
868
+ } = options;
869
+ debug('change owners of %o', name);
870
+ const {
871
+ username
872
+ } = requestOptions;
873
+ if (!username) {
874
+ throw _core.errorUtils.getBadRequest('update owners only allowed for logged in users');
875
+ }
876
+ if (!maintainers || maintainers.length === 0) {
877
+ throw _core.errorUtils.getBadRequest('maintainers field is required and must not be empty');
878
+ }
879
+ const localPackage = await this.getPackageManifest({
880
+ name,
881
+ requestOptions,
882
+ uplinksLook: false
883
+ });
884
+ await this.changePackage(name, {
885
+ ...localPackage,
886
+ maintainers: maintainers
887
+ }, options.revision);
888
+ return _core.API_MESSAGE.PKG_CHANGED;
889
+ }
903
890
 
904
891
  /**
905
892
  * Get local package, on fails return null.
@@ -957,7 +944,8 @@ class Storage {
957
944
  const {
958
945
  name
959
946
  } = options;
960
- debug('publishing a new package for %o', name);
947
+ const username = options.requestOptions.username;
948
+ debug('publishing a new package for %o as %o', name, username);
961
949
  let successResponseMessage;
962
950
  const manifest = {
963
951
  ..._core.validatioUtils.normalizeMetadata(body, name)
@@ -982,47 +970,51 @@ class Storage {
982
970
  const [firstAttachmentKey] = Object.keys(_attachments);
983
971
  const buffer = this.getBufferManifest(body._attachments[firstAttachmentKey].data);
984
972
  try {
985
- var _this$config2, _this$config2$publish;
973
+ var _this$config2;
986
974
  // we check if package exist already locally
987
- const manifest = await this.getPackagelocalByNameNext(name);
975
+ const localManifest = await this.getPackagelocalByNameNext(name);
988
976
  // if continue, the version to be published does not exist
989
- if ((manifest === null || manifest === void 0 ? void 0 : manifest.versions[versionToPublish]) != null) {
990
- debug('%s version %s already exists', name, versionToPublish);
977
+ if ((localManifest === null || localManifest === void 0 ? void 0 : localManifest.versions[versionToPublish]) != null) {
978
+ debug('%s version %s already exists (locally)', name, versionToPublish);
991
979
  throw _core.errorUtils.getConflict();
992
980
  }
993
- const uplinksLook = ((_this$config2 = this.config) === null || _this$config2 === void 0 ? void 0 : (_this$config2$publish = _this$config2.publish) === null || _this$config2$publish === void 0 ? void 0 : _this$config2$publish.allow_offline) === false;
981
+ const uplinksLook = ((_this$config2 = this.config) === null || _this$config2 === void 0 || (_this$config2 = _this$config2.publish) === null || _this$config2 === void 0 ? void 0 : _this$config2.allow_offline) === false;
994
982
  // if execution get here, package does not exist locally, we search upstream
995
983
  const remoteManifest = await this.checkPackageRemote(name, uplinksLook);
996
984
  if ((remoteManifest === null || remoteManifest === void 0 ? void 0 : remoteManifest.versions[versionToPublish]) != null) {
997
- debug('%s version %s already exists', name, versionToPublish);
985
+ debug('%s version %s already exists (upstream)', name, versionToPublish);
998
986
  throw _core.errorUtils.getConflict();
999
987
  }
1000
988
  const hasPackageInStorage = await this.hasPackage(name);
1001
989
  if (!hasPackageInStorage) {
1002
- await this.createNewLocalCachePackage(name, versionToPublish);
990
+ await this.createNewLocalCachePackage(name, username);
1003
991
  successResponseMessage = _core.API_MESSAGE.PKG_CREATED;
1004
992
  } else {
993
+ await this.checkAllowedToChangePackage(localManifest, username);
1005
994
  successResponseMessage = _core.API_MESSAGE.PKG_CHANGED;
1006
995
  }
1007
996
  } catch (err) {
1008
997
  debug('error on change or update a package with %o', err.message);
1009
- _logger.logger.error({
998
+ this.logger.error({
1010
999
  err: err.message
1011
- }, 'error on create package: @{err}');
1000
+ }, 'error on publish new version: @{err}');
1012
1001
  throw err;
1013
1002
  }
1014
1003
 
1015
1004
  // 1. after tarball has been successfully uploaded, we update the version
1016
1005
  try {
1017
- // TODO: review why do this
1018
- versions[versionToPublish].readme = _lodash.default.isNil(manifest.readme) === false ? String(manifest.readme) : '';
1019
- await this.addVersion(name, versionToPublish, versions[versionToPublish], null);
1006
+ const tarballStats = await this.getTarballStats(versions[versionToPublish], buffer);
1007
+ // Older package managers like npm6 do not send readme content as part of version but include it on root level
1008
+ if (_lodash.default.isEmpty(versions[versionToPublish].readme)) {
1009
+ versions[versionToPublish].readme = _lodash.default.isNil(manifest.readme) === false ? String(manifest.readme) : '';
1010
+ }
1011
+ // addVersion will move the readme from the the published version to the root level
1012
+ await this.addVersion(name, versionToPublish, versions[versionToPublish], null, tarballStats);
1020
1013
  } catch (err) {
1021
- _logger.logger.error({
1014
+ this.logger.error({
1022
1015
  err: err.message
1023
1016
  }, 'updated version has failed: @{err}');
1024
1017
  debug('error on create a version for %o with error %o', name, err.message);
1025
- // TODO: remove tarball if add version fails
1026
1018
  throw err;
1027
1019
  }
1028
1020
 
@@ -1033,16 +1025,15 @@ class Storage {
1033
1025
  // 1. add version
1034
1026
  // 2. merge versions
1035
1027
  // 3. upload tarball
1036
- // 3.update once to the storage (easy peasy)
1028
+ // 4. update once to the storage (easy peasy)
1037
1029
  mergedManifest = await this.mergeTagsNext(name, manifest[_core.DIST_TAGS]);
1038
1030
  } catch (err) {
1039
- _logger.logger.error({
1031
+ this.logger.error({
1040
1032
  err: err.message
1041
1033
  }, 'merge version has failed: @{err}');
1042
1034
  debug('error on create a version for %o with error %o', name, err.message);
1043
1035
  // TODO: undo if this fails
1044
- // 1. remove tarball
1045
- // 2. remove updated version
1036
+ // 1. remove updated version
1046
1037
  throw err;
1047
1038
  }
1048
1039
 
@@ -1053,12 +1044,15 @@ class Storage {
1053
1044
  signal: options.signal
1054
1045
  });
1055
1046
  } catch (err) {
1056
- _logger.logger.error({
1047
+ this.logger.error({
1057
1048
  err: err.message
1058
1049
  }, 'upload tarball has failed: @{err}');
1050
+ // TODO: undo if this fails
1051
+ // 1. remove updated version
1052
+ // 2. remove new dist tags
1059
1053
  throw err;
1060
1054
  }
1061
- _logger.logger.info({
1055
+ this.logger.info({
1062
1056
  name,
1063
1057
  version: versionToPublish
1064
1058
  }, 'package @{name}@@{version} has been published');
@@ -1198,22 +1192,31 @@ class Storage {
1198
1192
  * @param metadata version metadata
1199
1193
  * @param tag tag of the version
1200
1194
  */
1201
- async addVersion(name, version, metadata, tag) {
1195
+ async addVersion(name, version, metadata, tag, tarballStats) {
1202
1196
  debug(`add version %s package for %s`, version, name);
1203
1197
  await this.updatePackage(name, async data => {
1204
1198
  // keep only one readme per package
1205
1199
  data.readme = metadata.readme;
1206
- debug('%s` readme mutated', name);
1200
+ debug('%s readme mutated', name);
1207
1201
  // TODO: lodash remove
1208
1202
  metadata = (0, _2.cleanUpReadme)(metadata);
1209
1203
  metadata.contributors = (0, _utils.normalizeContributors)(metadata.contributors);
1210
- debug('%s` contributors normalized', name);
1204
+ debug('%s contributors normalized', name);
1205
+
1206
+ // Copy current owners to version
1207
+ metadata.maintainers = data.maintainers;
1208
+
1209
+ // Update tarball stats
1210
+ if (metadata.dist) {
1211
+ metadata.dist.fileCount = tarballStats.fileCount;
1212
+ metadata.dist.unpackedSize = tarballStats.unpackedSize;
1213
+ }
1211
1214
 
1212
1215
  // if uploaded tarball has a different shasum, it's very likely that we
1213
1216
  // have some kind of error
1214
1217
  if (_core.validatioUtils.isObject(metadata.dist) && _lodash.default.isString(metadata.dist.tarball)) {
1215
1218
  var _data$time;
1216
- const tarball = metadata.dist.tarball.replace(/.*\//, '');
1219
+ const tarball = (0, _tarball.extractTarballFromUrl)(metadata.dist.tarball);
1217
1220
  if (_core.validatioUtils.isObject(data._attachments[tarball])) {
1218
1221
  if (_lodash.default.isNil(data._attachments[tarball].shasum) === false && _lodash.default.isNil(metadata.dist.shasum) === false) {
1219
1222
  if (data._attachments[tarball].shasum != metadata.dist.shasum) {
@@ -1254,7 +1257,7 @@ class Storage {
1254
1257
  // TODO: review this method, it's a bit ugly
1255
1258
  (0, _2.tagVersion)(data, version, tag);
1256
1259
  try {
1257
- debug('%s` add on database', name);
1260
+ debug('%s add on database', name);
1258
1261
  await this.localStorage.getStoragePlugin().add(name);
1259
1262
  this.logger.debug({
1260
1263
  name,
@@ -1272,7 +1275,7 @@ class Storage {
1272
1275
  * @param name name of the package
1273
1276
  * @returns
1274
1277
  */
1275
- async createNewLocalCachePackage(name, latestVersion) {
1278
+ async createNewLocalCachePackage(name, username) {
1276
1279
  const storage = this.getPrivatePackageStorage(name);
1277
1280
  if (!storage) {
1278
1281
  debug(`storage is missing for %o package cannot be added`, name);
@@ -1283,10 +1286,19 @@ class Storage {
1283
1286
  ...(0, _storageUtils.generatePackageTemplate)(name),
1284
1287
  time: {
1285
1288
  created: currentTime,
1286
- modified: currentTime,
1287
- [latestVersion]: currentTime
1289
+ modified: currentTime
1288
1290
  }
1289
1291
  };
1292
+
1293
+ // Set initial package owner
1294
+ // TODO: Add email of user
1295
+ packageData.maintainers = username && username.length > 0 ? [{
1296
+ name: username,
1297
+ email: ''
1298
+ }] : [{
1299
+ name: _core.DEFAULT_USER,
1300
+ email: ''
1301
+ }];
1290
1302
  try {
1291
1303
  await storage.createPackage(name, packageData);
1292
1304
  this.logger.info({
@@ -1311,7 +1323,7 @@ class Storage {
1311
1323
  async checkPackageRemote(name, uplinksLook) {
1312
1324
  try {
1313
1325
  // we provide a null manifest, thus the manifest returned will be the remote one
1314
- const [remoteManifest, upLinksErrors] = await this.syncUplinksMetadataNext(name, null, {
1326
+ const [remoteManifest, upLinksErrors] = await this.syncUplinksMetadata(name, null, {
1315
1327
  uplinksLook
1316
1328
  });
1317
1329
 
@@ -1416,7 +1428,7 @@ class Storage {
1416
1428
  // if we can't get the local metadata, we try to get the remote metadata
1417
1429
  // if we do to have local metadata, we try to update it with the upstream registry
1418
1430
  debug('sync uplinks for %o', name);
1419
- const [remoteManifest, upLinksErrors] = await this.syncUplinksMetadataNext(name, data, {
1431
+ const [remoteManifest, upLinksErrors] = await this.syncUplinksMetadata(name, data, {
1420
1432
  uplinksLook: options.uplinksLook,
1421
1433
  retry: options.retry,
1422
1434
  remoteAddress: options.requestOptions.remoteAddress
@@ -1438,7 +1450,7 @@ class Storage {
1438
1450
  ...(0, _storageUtils.normalizeDistTags)((0, _storageUtils.cleanUpLinksRef)(remoteManifest, options.keepUpLinkData)),
1439
1451
  _attachments: {}
1440
1452
  });
1441
- debug('no. sync uplinks errors %o for %s', upLinksErrors === null || upLinksErrors === void 0 ? void 0 : upLinksErrors.length, name);
1453
+ debug('no sync uplinks errors %o for %s', upLinksErrors === null || upLinksErrors === void 0 ? void 0 : upLinksErrors.length, name);
1442
1454
  return [normalizedPkg, upLinksErrors];
1443
1455
  }
1444
1456
 
@@ -1462,7 +1474,7 @@ class Storage {
1462
1474
  in that case the request returns empty body and we want ask next on the list if has fresh
1463
1475
  updates.
1464
1476
  */
1465
- async syncUplinksMetadataNext(name, localManifest, options = {}) {
1477
+ async syncUplinksMetadata(name, localManifest, options = {}) {
1466
1478
  let found = localManifest !== null;
1467
1479
  let syncManifest = null;
1468
1480
  const upLinks = [];
@@ -1578,7 +1590,7 @@ class Storage {
1578
1590
  // merge time field cache and remote
1579
1591
  _cacheManifest = (0, _storageUtils.mergeUplinkTimeIntoLocalNext)(_cacheManifest, remoteManifest);
1580
1592
  // update the _uplinks field in the cache
1581
- _cacheManifest = (0, _uplinkUtil.updateVersionsHiddenUpLinkNext)(_cacheManifest, uplink);
1593
+ _cacheManifest = (0, _proxy.updateVersionsHiddenUpLinkNext)(_cacheManifest, uplink);
1582
1594
  try {
1583
1595
  // merge versions from remote into the cache
1584
1596
  _cacheManifest = (0, _storageUtils.mergeVersions)(_cacheManifest, remoteManifest);
@@ -1697,7 +1709,7 @@ class Storage {
1697
1709
  for (const versionId in remoteManifest.versions) {
1698
1710
  // if detect a new remote version does not exist cache
1699
1711
  if (_lodash.default.isNil(cacheManifest.versions[versionId])) {
1700
- var _version2, _version2$dist;
1712
+ var _version2;
1701
1713
  debug('new version from upstream %o', versionId);
1702
1714
  let version = remoteManifest.versions[versionId];
1703
1715
 
@@ -1709,7 +1721,7 @@ class Storage {
1709
1721
  version.contributors = (0, _utils.normalizeContributors)(version.contributors);
1710
1722
  change = true;
1711
1723
  cacheManifest.versions[versionId] = version;
1712
- if ((_version2 = version) !== null && _version2 !== void 0 && (_version2$dist = _version2.dist) !== null && _version2$dist !== void 0 && _version2$dist.tarball) {
1724
+ if ((_version2 = version) !== null && _version2 !== void 0 && (_version2 = _version2.dist) !== null && _version2 !== void 0 && _version2.tarball) {
1713
1725
  const filename = _core.pkgUtils.extractTarballName(version.dist.tarball);
1714
1726
  // store a fast access to the dist file by tarball name
1715
1727
  // it does NOT overwrite any existing records
@@ -1758,6 +1770,31 @@ class Storage {
1758
1770
  return cacheManifest;
1759
1771
  }
1760
1772
  }
1773
+ async getTarballStats(version, buffer) {
1774
+ var _version$dist, _version$dist2;
1775
+ if (version.dist == undefined || ((_version$dist = version.dist) === null || _version$dist === void 0 ? void 0 : _version$dist.fileCount) == undefined || ((_version$dist2 = version.dist) === null || _version$dist2 === void 0 ? void 0 : _version$dist2.unpackedSize) == undefined) {
1776
+ debug('tarball stats not found, calculating');
1777
+ return await (0, _tarball.getTarballDetails)(buffer);
1778
+ } else {
1779
+ debug('tarball stats found');
1780
+ return {
1781
+ fileCount: version.dist.fileCount,
1782
+ unpackedSize: version.dist.unpackedSize
1783
+ };
1784
+ }
1785
+ }
1786
+ async checkAllowedToChangePackage(manifest, username) {
1787
+ var _this$config3;
1788
+ // Checks to perform if config "publish:check_owners" is true
1789
+ debug('check if user %o is an owner and allowed to change package', username);
1790
+ // if name of owner is not included in list of maintainers, then throw an error
1791
+ if (((_this$config3 = this.config) === null || _this$config3 === void 0 || (_this$config3 = _this$config3.publish) === null || _this$config3 === void 0 ? void 0 : _this$config3.check_owners) === true && manifest.maintainers && manifest.maintainers.length > 0 && !manifest.maintainers.some(maintainer => maintainer.name === username)) {
1792
+ this.logger.error({
1793
+ username
1794
+ }, '@{username} is not a maintainer (package owner)');
1795
+ throw Error('only owners are allowed to change package');
1796
+ }
1797
+ }
1761
1798
  }
1762
1799
  exports.Storage = Storage;
1763
1800
  //# sourceMappingURL=storage.js.map