@verdaccio/store 6.0.0-6-next.21 → 6.0.0-6-next.24

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 (72) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/build/index.d.ts +5 -4
  3. package/build/index.js +31 -15
  4. package/build/index.js.map +1 -1
  5. package/build/lib/TransFormResults.d.ts +15 -0
  6. package/build/lib/TransFormResults.js +61 -0
  7. package/build/lib/TransFormResults.js.map +1 -0
  8. package/build/lib/search-utils.d.ts +4 -0
  9. package/build/lib/search-utils.js +59 -0
  10. package/build/lib/search-utils.js.map +1 -0
  11. package/build/lib/star-utils.d.ts +14 -0
  12. package/build/{star-utils.js → lib/star-utils.js} +16 -2
  13. package/build/lib/star-utils.js.map +1 -0
  14. package/build/lib/storage-utils.d.ts +82 -0
  15. package/build/{storage-utils.js → lib/storage-utils.js} +173 -60
  16. package/build/lib/storage-utils.js.map +1 -0
  17. package/build/lib/uplink-util.d.ts +10 -0
  18. package/build/lib/uplink-util.js +49 -0
  19. package/build/lib/uplink-util.js.map +1 -0
  20. package/build/lib/versions-utils.d.ts +28 -0
  21. package/build/lib/versions-utils.js +104 -0
  22. package/build/lib/versions-utils.js.map +1 -0
  23. package/build/local-storage.d.ts +3 -165
  24. package/build/local-storage.js +6 -1166
  25. package/build/local-storage.js.map +1 -1
  26. package/build/storage.d.ts +242 -86
  27. package/build/storage.js +1681 -548
  28. package/build/storage.js.map +1 -1
  29. package/build/type.d.ts +42 -19
  30. package/build/type.js.map +1 -1
  31. package/jest.config.js +8 -2
  32. package/package.json +71 -71
  33. package/src/index.ts +5 -4
  34. package/src/lib/TransFormResults.ts +42 -0
  35. package/src/lib/search-utils.ts +50 -0
  36. package/src/{star-utils.ts → lib/star-utils.ts} +16 -5
  37. package/src/lib/storage-utils.ts +362 -0
  38. package/src/lib/uplink-util.ts +41 -0
  39. package/src/lib/versions-utils.ts +87 -0
  40. package/src/local-storage.ts +7 -1217
  41. package/src/storage.ts +1683 -635
  42. package/src/type.ts +47 -21
  43. package/test/fixtures/config/getTarballNext-getupstream.yaml +17 -0
  44. package/test/fixtures/config/syncDoubleUplinksMetadata.yaml +25 -0
  45. package/test/fixtures/config/syncNoUplinksMetadata.yaml +17 -0
  46. package/test/fixtures/config/syncSingleUplinksMetadata.yaml +21 -0
  47. package/test/fixtures/config/updateManifest-1.yaml +16 -0
  48. package/test/fixtures/manifests/foo-npmjs.json +253 -0
  49. package/test/fixtures/manifests/foo-verdaccio.json +253 -0
  50. package/test/fixtures/tarball.tgz +0 -0
  51. package/test/helpers.ts +22 -0
  52. package/test/local-store.__disabled__.ts +553 -0
  53. package/test/search.spec.ts +4 -5
  54. package/test/star.spec.ts +31 -0
  55. package/test/storage-utils.spec.ts +129 -42
  56. package/test/storage.spec.ts +943 -33
  57. package/test/versions.spec.ts +109 -0
  58. package/tsconfig.json +6 -3
  59. package/build/search.d.ts +0 -35
  60. package/build/search.js +0 -198
  61. package/build/search.js.map +0 -1
  62. package/build/star-utils.d.ts +0 -9
  63. package/build/star-utils.js.map +0 -1
  64. package/build/storage-utils.d.ts +0 -39
  65. package/build/storage-utils.js.map +0 -1
  66. package/build/uplink-util.d.ts +0 -7
  67. package/build/uplink-util.js +0 -38
  68. package/build/uplink-util.js.map +0 -1
  69. package/src/search.ts +0 -175
  70. package/src/storage-utils.ts +0 -263
  71. package/src/uplink-util.ts +0 -32
  72. package/test/local-storage.spec.ts +0 -583
package/build/storage.js CHANGED
@@ -3,15 +3,19 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.Storage = void 0;
6
+ exports.resourceNotAvailable = exports.noSuchFile = exports.Storage = exports.PROTO_NAME = void 0;
7
7
 
8
8
  var _assert = _interopRequireDefault(require("assert"));
9
9
 
10
- var _async = _interopRequireDefault(require("async"));
11
-
12
10
  var _debug = _interopRequireDefault(require("debug"));
13
11
 
14
- var _lodash = _interopRequireDefault(require("lodash"));
12
+ var _lodash = _interopRequireWildcard(require("lodash"));
13
+
14
+ var _stream = require("stream");
15
+
16
+ var _promises = require("stream/promises");
17
+
18
+ var _url = _interopRequireDefault(require("url"));
15
19
 
16
20
  var _config = require("@verdaccio/config");
17
21
 
@@ -21,385 +25,443 @@ var _logger = require("@verdaccio/logger");
21
25
 
22
26
  var _proxy = require("@verdaccio/proxy");
23
27
 
24
- var _streams = require("@verdaccio/streams");
25
-
26
28
  var _tarball = require("@verdaccio/tarball");
27
29
 
28
30
  var _utils = require("@verdaccio/utils");
29
31
 
30
- var _localStorage = require("./local-storage");
32
+ var _2 = require(".");
33
+
34
+ var _TransFormResults = require("./lib/TransFormResults");
35
+
36
+ var _searchUtils = require("./lib/search-utils");
31
37
 
32
- var _search = require("./search");
38
+ var _starUtils = require("./lib/star-utils");
33
39
 
34
- var _storageUtils = require("./storage-utils");
40
+ var _storageUtils = require("./lib/storage-utils");
35
41
 
36
- var _uplinkUtil = require("./uplink-util");
42
+ var _uplinkUtil = require("./lib/uplink-util");
43
+
44
+ var _versionsUtils = require("./lib/versions-utils");
45
+
46
+ var _localStorage = require("./local-storage");
47
+
48
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
49
+
50
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
37
51
 
38
52
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
39
53
 
40
- // import { isPublishablePackage, validateInputs } from './star-utils';
41
- // import { StarBody, Users } from './type';
42
54
  const debug = (0, _debug.default)('verdaccio:storage');
55
+ const noSuchFile = 'ENOENT';
56
+ exports.noSuchFile = noSuchFile;
57
+ const resourceNotAvailable = 'EAGAIN';
58
+ exports.resourceNotAvailable = resourceNotAvailable;
59
+ const PROTO_NAME = '__proto__';
60
+ exports.PROTO_NAME = PROTO_NAME;
43
61
 
44
62
  class Storage {
45
63
  constructor(config) {
46
64
  this.config = config;
47
65
  this.uplinks = (0, _uplinkUtil.setupUpLinks)(config);
48
- debug('uplinks available %o', Object.keys(this.uplinks));
49
66
  this.logger = _logger.logger.child({
50
67
  module: 'storage'
51
68
  });
52
69
  this.filters = []; // @ts-ignore
53
70
 
54
71
  this.localStorage = null;
55
- this.searchManager = null;
72
+ debug('uplinks available %o', Object.keys(this.uplinks));
56
73
  }
57
74
 
58
- async init(config, filters = []) {
59
- if (this.localStorage === null) {
60
- this.filters = filters || [];
61
- debug('filters available %o', filters);
62
- this.localStorage = new _localStorage.LocalStorage(this.config, _logger.logger);
63
- await this.localStorage.init();
64
- debug('local init storage initialized');
65
- await this.localStorage.getSecret(config);
66
- debug('local storage secret initialized');
67
- this.searchManager = new _search.SearchManager(this.uplinks, this.localStorage);
68
- } else {
69
- debug('storage has been already initialized');
70
- }
71
-
72
- return;
73
- }
75
+ static ABBREVIATED_HEADER = 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*';
74
76
  /**
75
- * Add a {name} package to a system
76
- Function checks if package with the same name is available from uplinks.
77
- If it isn't, we create package locally
78
- Used storages: local (write) && uplinks
77
+ * Change an existing package (i.e. unpublish one version)
78
+ Function changes a package info from local storage and all uplinks with write access./
79
+ Used storages: local (write)
79
80
  */
80
81
 
82
+ async changePackageNext(name, metadata, revision) {
83
+ debug('change existing package for package %o revision %o', name, revision);
84
+ debug(`change manifest tags for %o revision %s`, name, revision);
81
85
 
82
- async addPackage(name, metadata, callback) {
83
- try {
84
- debug('add package for %o', name);
85
- await (0, _storageUtils.checkPackageLocal)(name, this.localStorage);
86
- debug('look up remote for %o', name);
87
- await (0, _storageUtils.checkPackageRemote)(name, this._isAllowPublishOffline(), this._syncUplinksMetadata.bind(this));
88
- debug('publishing a package for %o', name);
89
- await (0, _storageUtils.publishPackage)(name, metadata, this.localStorage); // TODO: return published data and replace callback by a promise
90
-
91
- callback(null, true);
92
- } catch (err) {
93
- debug('error on add a package for %o with error %o', name, err);
94
- callback(err);
86
+ if (!_core.validatioUtils.isObject(metadata.versions) || !_core.validatioUtils.isObject(metadata[_core.DIST_TAGS])) {
87
+ debug(`change manifest bad data for %o`, name);
88
+ throw _core.errorUtils.getBadData();
95
89
  }
96
- }
97
- /**
98
- * Add a {name} package to a system
99
- Function checks if package with the same name is available from uplinks.
100
- If it isn't, we create package locally
101
- Used storages: local (write) && uplinks
102
- */
103
90
 
91
+ debug(`change manifest udapting manifest for %o`, name);
92
+ await this.updatePackageNext(name, async localData => {
93
+ // eslint-disable-next-line guard-for-in
94
+ for (const version in localData.versions) {
95
+ const incomingVersion = metadata.versions[version];
104
96
 
105
- async addPackageNext(name, metadata) {
106
- try {
107
- debug('add package for %o', name);
108
- await (0, _storageUtils.checkPackageLocal)(name, this.localStorage);
109
- debug('look up remote for %o', name);
110
- await (0, _storageUtils.checkPackageRemote)(name, this._isAllowPublishOffline(), this._syncUplinksMetadata.bind(this));
111
- debug('publishing a package for %o', name); // FIXME: publishPackage should return fresh metadata from backend
112
- // instead return metadata
113
-
114
- await (0, _storageUtils.publishPackage)(name, metadata, this.localStorage);
115
- return metadata;
116
- } catch (err) {
117
- debug('error on add a package for %o with error %o', name, err);
118
- throw err;
119
- }
120
- }
97
+ if (_lodash.default.isNil(incomingVersion)) {
98
+ this.logger.info({
99
+ name: name,
100
+ version: version
101
+ }, 'unpublishing @{name}@@{version}'); // FIXME: I prefer return a new object rather mutate the metadata
121
102
 
122
- _isAllowPublishOffline() {
123
- return typeof this.config.publish !== 'undefined' && _lodash.default.isBoolean(this.config.publish.allow_offline) && this.config.publish.allow_offline;
124
- }
103
+ delete localData.versions[version];
104
+ delete localData.time[version];
125
105
 
126
- readTokens(filter) {
127
- return this.localStorage.readTokens(filter);
128
- }
106
+ for (const file in localData._attachments) {
107
+ if (localData._attachments[file].version === version) {
108
+ delete localData._attachments[file].version;
109
+ }
110
+ }
111
+ } else if (Object.prototype.hasOwnProperty.call(incomingVersion, 'deprecated')) {
112
+ const incomingDeprecated = incomingVersion.deprecated;
113
+
114
+ if (incomingDeprecated != localData.versions[version].deprecated) {
115
+ if (!incomingDeprecated) {
116
+ this.logger.info({
117
+ name: name,
118
+ version: version
119
+ }, 'undeprecating @{name}@@{version}');
120
+ delete localData.versions[version].deprecated;
121
+ } else {
122
+ this.logger.info({
123
+ name: name,
124
+ version: version
125
+ }, 'deprecating @{name}@@{version}');
126
+ localData.versions[version].deprecated = incomingDeprecated;
127
+ }
129
128
 
130
- saveToken(token) {
131
- return this.localStorage.saveToken(token);
129
+ localData.time.modified = new Date().toISOString();
130
+ }
131
+ }
132
+ }
133
+
134
+ localData[_core.USERS] = metadata[_core.USERS];
135
+ localData[_core.DIST_TAGS] = metadata[_core.DIST_TAGS];
136
+ return localData;
137
+ });
132
138
  }
133
139
 
134
- deleteToken(user, tokenKey) {
135
- return this.localStorage.deleteToken(user, tokenKey);
140
+ async removePackage(name, revision) {
141
+ debug('remove package %o', name);
142
+ await this.removePackageByRevision(name, revision);
136
143
  }
137
144
  /**
138
- * Add a new version of package {name} to a system
139
- Used storages: local (write)
145
+ Remove a tarball from a system
146
+ Function removes a tarball from local storage.
147
+ Tarball in question should not be linked to in any existing
148
+ versions, i.e. package version should be unpublished first.
149
+ Used storage: local (write)
140
150
  */
151
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
141
152
 
142
153
 
143
- addVersion(name, version, metadata, tag, callback) {
144
- debug('add the version %o for package %o', version, name);
145
- this.localStorage.addVersion(name, version, metadata, tag, callback);
146
- }
154
+ async removeTarball(name, filename, _revision) {
155
+ debug('remove tarball %s for %s', filename, name);
156
+ (0, _assert.default)(_core.validatioUtils.validateName(filename));
157
+ const storage = this.getPrivatePackageStorage(name);
147
158
 
148
- async addVersionNext(name, version, metadata, tag) {
149
- debug('add the version %o for package %o', version, name);
150
- return this.localStorage.addVersionNext(name, version, metadata, tag);
151
- }
152
- /**
153
- * Tags a package version with a provided tag
154
- Used storages: local (write)
155
- */
159
+ if (!storage) {
160
+ debug(`method not implemented for storage`);
161
+ this.logger.error('method for remove tarball not implemented');
162
+ throw _core.errorUtils.getInternalError(_core.API_ERROR.INTERNAL_SERVER_ERROR);
163
+ }
156
164
 
165
+ try {
166
+ const cacheManifest = await storage.readPackage(name);
157
167
 
158
- mergeTags(name, tagHash, callback) {
159
- debug('merge tags for package %o tags %o', name, tagHash);
160
- this.localStorage.mergeTags(name, tagHash, callback);
161
- }
162
- /**
163
- * Change an existing package (i.e. unpublish one version)
164
- Function changes a package info from local storage and all uplinks with write access./
165
- Used storages: local (write)
166
- */
168
+ if (!cacheManifest._attachments[filename]) {
169
+ throw _core.errorUtils.getNotFound('no such file available');
170
+ }
171
+ } catch (err) {
172
+ if (err.code === noSuchFile) {
173
+ throw _core.errorUtils.getNotFound();
174
+ }
167
175
 
176
+ throw err;
177
+ }
168
178
 
169
- changePackage(name, metadata, revision, callback) {
170
- debug('change existing package for package %o revision %o', name, revision);
171
- this.localStorage.changePackage(name, metadata, revision, callback);
172
- }
173
- /**
174
- * Change an existing package (i.e. unpublish one version)
175
- Function changes a package info from local storage and all uplinks with write access./
176
- Used storages: local (write)
177
- */
179
+ const manifest = await this.updatePackageNext(name, async data => {
180
+ let newData = { ...data
181
+ };
182
+ delete data._attachments[filename];
183
+ return newData;
184
+ });
178
185
 
186
+ try {
187
+ const storage = this.getPrivatePackageStorage(name);
179
188
 
180
- async changePackageNext(name, metadata, revision) {
181
- debug('change existing package for package %o revision %o', name, revision);
182
- this.localStorage.changePackageNext(name, metadata, revision);
183
- }
184
- /**
185
- * Remove a package from a system
186
- Function removes a package from local storage
187
- Used storages: local (write)
188
- */
189
+ if (!storage) {
190
+ debug(`method not implemented for storage`);
191
+ this.logger.error('method for remove tarball not implemented');
192
+ throw _core.errorUtils.getInternalError(_core.API_ERROR.INTERNAL_SERVER_ERROR);
193
+ }
189
194
 
195
+ await storage.deletePackage(filename);
196
+ debug('package %s removed', filename);
197
+ } catch (err) {
198
+ this.logger.error({
199
+ err
200
+ }, 'error removing %s from storage');
201
+ throw err;
202
+ }
190
203
 
191
- async removePackage(name) {
192
- debug('remove packagefor package %o', name);
193
- await this.localStorage.removePackage(name);
204
+ return manifest;
194
205
  }
195
206
  /**
196
- Remove a tarball from a system
197
- Function removes a tarball from local storage.
198
- Tarball in question should not be linked to in any existing
199
- versions, i.e. package version should be unpublished first.
200
- Used storage: local (write)
207
+ * Handle search on packages and proxies.
208
+ * Iterate all proxies configured and search in all endpoints in v2 and pipe all responses
209
+ * to a stream, once the proxies request has finished search in local storage for all packages
210
+ * (privated and cached).
201
211
  */
202
212
 
203
213
 
204
- removeTarball(name, filename, revision, callback) {
205
- this.localStorage.removeTarball(name, filename, revision, callback);
206
- }
207
- /**
208
- * Upload a tarball for {name} package
209
- Function is synchronous and returns a WritableStream
210
- Used storages: local (write)
211
- */
214
+ async search(options) {
215
+ const transformResults = new _TransFormResults.TransFormResults({
216
+ objectMode: true
217
+ });
218
+ const streamPassThrough = new _stream.PassThrough({
219
+ objectMode: true
220
+ });
221
+ const upLinkList = this.getProxyList();
222
+ const searchUplinksStreams = upLinkList.map(uplinkId => {
223
+ const uplink = this.uplinks[uplinkId];
224
+
225
+ if (!uplink) {
226
+ // this should never tecnically happens
227
+ this.logger.error({
228
+ uplinkId
229
+ }, 'uplink @upLinkId not found');
230
+ }
231
+
232
+ return this.consumeSearchStream(uplinkId, uplink, options, streamPassThrough);
233
+ });
212
234
 
235
+ try {
236
+ debug('search uplinks'); // we only process those streams end successfully, if all fails
237
+ // we just include local storage
238
+
239
+ await Promise.allSettled([...searchUplinksStreams]);
240
+ debug('search uplinks done');
241
+ } catch (err) {
242
+ this.logger.error({
243
+ err: err === null || err === void 0 ? void 0 : err.message
244
+ }, ' error on uplinks search @{err}');
245
+ streamPassThrough.emit('error', err);
246
+ }
247
+
248
+ debug('search local');
249
+
250
+ try {
251
+ await this.searchCachedPackages(streamPassThrough, options.query);
252
+ } catch (err) {
253
+ this.logger.error({
254
+ err: err === null || err === void 0 ? void 0 : err.message
255
+ }, ' error on local search @{err}');
256
+ streamPassThrough.emit('error', err);
257
+ }
213
258
 
214
- addTarball(name, filename) {
215
- debug('add tarball for package %o', name);
216
- return this.localStorage.addTarball(name, filename);
259
+ const data = [];
260
+ const outPutStream = new _stream.PassThrough({
261
+ objectMode: true
262
+ });
263
+ (0, _stream.pipeline)(streamPassThrough, transformResults, outPutStream, err => {
264
+ if (err) {
265
+ this.logger.error({
266
+ err: err === null || err === void 0 ? void 0 : err.message
267
+ }, ' error on search @{err}');
268
+ throw _core.errorUtils.getInternalError(err ? err.message : 'unknown search error');
269
+ } else {
270
+ debug('pipeline succeeded');
271
+ }
272
+ });
273
+ outPutStream.on('data', chunk => {
274
+ data.push(chunk);
275
+ });
276
+ return new Promise(resolve => {
277
+ outPutStream.on('finish', async () => {
278
+ const searchFinalResults = (0, _searchUtils.removeDuplicates)(data);
279
+ debug('search stream total results: %o', searchFinalResults.length);
280
+ return resolve(searchFinalResults);
281
+ });
282
+ debug('search done');
283
+ });
217
284
  }
218
- /**
219
- Get a tarball from a storage for {name} package
220
- Function is synchronous and returns a ReadableStream
221
- Function tries to read tarball locally, if it fails then it reads package
222
- information in order to figure out where we can get this tarball from
223
- Used storages: local || uplink (just one)
224
- */
225
285
 
286
+ async getTarballFromUpstream(name, filename, {
287
+ signal
288
+ }) {
289
+ var _cachedManifest, _cachedManifest2, _cachedManifest2$_dis;
226
290
 
227
- getTarball(name, filename) {
228
- debug('get tarball for package %o filename %o', name, filename);
229
- const readStream = new _streams.ReadTarball({});
291
+ let cachedManifest = null;
230
292
 
231
- readStream.abort = function () {};
293
+ try {
294
+ cachedManifest = await this.getPackageLocalMetadata(name);
295
+ } catch (err) {
296
+ debug('error on get package local metadata %o', err);
297
+ } // dist url should be on local cache metadata
232
298
 
233
- const self = this; // if someone requesting tarball, it means that we should already have some
234
- // information about it, so fetching package info is unnecessary
235
- // trying local first
236
- // flow: should be IReadTarball
237
299
 
238
- let localStream = self.localStorage.getTarball(name, filename);
239
- let isOpen = false;
240
- localStream.on('error', err => {
241
- if (isOpen || err.status !== _core.HTTP_STATUS.NOT_FOUND) {
242
- return readStream.emit('error', err);
243
- } // local reported 404
244
-
245
-
246
- const err404 = err;
247
- localStream.abort();
248
- localStream = null; // we force for garbage collector
249
-
250
- self.localStorage.getPackageMetadata(name, (err, info) => {
251
- if (_lodash.default.isNil(err) && info._distfiles && _lodash.default.isNil(info._distfiles[filename]) === false) {
252
- // information about this file exists locally
253
- serveFile(info._distfiles[filename]);
254
- } else {
255
- // we know nothing about this file, trying to get information elsewhere
256
- self._syncUplinksMetadata(name, info, {}, (err, info) => {
257
- if (_lodash.default.isNil(err) === false) {
258
- return readStream.emit('error', err);
259
- }
300
+ 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') {
301
+ var _cachedManifest3;
260
302
 
261
- if (_lodash.default.isNil(info._distfiles) || _lodash.default.isNil(info._distfiles[filename])) {
262
- return readStream.emit('error', err404);
263
- }
303
+ debug('dist file found, using it %o', (_cachedManifest3 = cachedManifest) === null || _cachedManifest3 === void 0 ? void 0 : _cachedManifest3._distfiles[filename].url); // dist file found, proceed to download
264
304
 
265
- serveFile(info._distfiles[filename]);
266
- });
305
+ const distFile = cachedManifest._distfiles[filename];
306
+ let current_length = 0;
307
+ let expected_length;
308
+ const passThroughRemoteStream = new _stream.PassThrough();
309
+ const proxy = this.getUpLinkForDistFile(name, distFile);
310
+ const remoteStream = proxy.fetchTarballNext(distFile.url, {});
311
+ remoteStream.on('request', async () => {
312
+ try {
313
+ debug('remote stream request');
314
+ const storage = this.getPrivatePackageStorage(name);
315
+
316
+ if (proxy.config.cache === true && storage) {
317
+ const localStorageWriteStream = await storage.writeTarball(filename, {
318
+ signal
319
+ });
320
+ await (0, _promises.pipeline)(remoteStream, passThroughRemoteStream, localStorageWriteStream, {
321
+ signal
322
+ });
323
+ } else {
324
+ await (0, _promises.pipeline)(remoteStream, passThroughRemoteStream, {
325
+ signal
326
+ });
327
+ }
328
+ } catch (err) {
329
+ debug('error on pipeline downloading tarball for package %o', name);
330
+ passThroughRemoteStream.emit('error', err);
267
331
  }
268
332
  });
269
- });
270
- localStream.on('content-length', function (v) {
271
- readStream.emit('content-length', v);
272
- });
273
- localStream.on('open', function () {
274
- isOpen = true;
275
- localStream.pipe(readStream);
276
- });
277
- return readStream;
278
- /**
279
- * Fetch and cache local/remote packages.
280
- * @param {Object} file define the package shape
281
- */
282
-
283
- function serveFile(file) {
284
- let uplink = null;
285
-
286
- for (const uplinkId in self.uplinks) {
287
- // https://github.com/verdaccio/verdaccio/issues/1642
288
- if ((0, _config.hasProxyTo)(name, uplinkId, self.config.packages)) {
289
- uplink = self.uplinks[uplinkId];
333
+ remoteStream.on('response', async res => {
334
+ if (res.statusCode === _core.HTTP_STATUS.NOT_FOUND) {
335
+ debug('remote stream response 404');
336
+ passThroughRemoteStream.emit('error', _core.errorUtils.getNotFound(_core.errorUtils.API_ERROR.NOT_FILE_UPLINK));
337
+ return;
290
338
  }
291
- }
292
339
 
293
- if (uplink == null) {
294
- uplink = new _proxy.ProxyStorage({
295
- url: file.url,
296
- cache: true,
297
- _autogenerated: true
298
- }, self.config);
299
- }
340
+ if (!(res.statusCode >= _core.HTTP_STATUS.OK && res.statusCode < _core.HTTP_STATUS.MULTIPLE_CHOICES)) {
341
+ debug('remote stream response ok');
342
+ passThroughRemoteStream.emit('error', _core.errorUtils.getInternalError(`bad uplink status code: ${res.statusCode}`));
343
+ return;
344
+ }
300
345
 
301
- let savestream = null;
346
+ if (res.headers[_core.HEADER_TYPE.CONTENT_LENGTH]) {
347
+ expected_length = res.headers[_core.HEADER_TYPE.CONTENT_LENGTH];
348
+ debug('remote stream response content length %o', expected_length);
349
+ passThroughRemoteStream.emit(_core.HEADER_TYPE.CONTENT_LENGTH, res.headers[_core.HEADER_TYPE.CONTENT_LENGTH]);
350
+ }
351
+ }).on('downloadProgress', progress => {
352
+ current_length = progress.transferred;
302
353
 
303
- if (uplink.config.cache) {
304
- savestream = self.localStorage.addTarball(name, filename);
305
- }
354
+ if (typeof expected_length === 'undefined' && progress.total) {
355
+ expected_length = progress.total;
356
+ }
357
+ }).on('end', () => {
358
+ if (expected_length && current_length != expected_length) {
359
+ debug('stream end, but length mismatch %o %o', current_length, expected_length);
360
+ passThroughRemoteStream.emit('error', _core.errorUtils.getInternalError(_core.API_ERROR.CONTENT_MISMATCH));
361
+ }
306
362
 
307
- let on_open = function () {
308
- // prevent it from being called twice
309
- on_open = function () {};
363
+ debug('remote stream end');
364
+ }).on('error', err => {
365
+ debug('remote stream error %o', err);
366
+ passThroughRemoteStream.emit('error', err);
367
+ });
368
+ return passThroughRemoteStream;
369
+ } else {
370
+ debug('dist file not found, proceed update upstream'); // no dist url found, proceed to fetch from upstream
371
+ // should not be the case
310
372
 
311
- const rstream2 = uplink.fetchTarball(file.url);
312
- rstream2.on('error', function (err) {
313
- if (savestream) {
314
- savestream.abort();
315
- }
373
+ const passThroughRemoteStream = new _stream.PassThrough(); // ensure get the latest data
316
374
 
317
- savestream = null;
318
- readStream.emit('error', err);
319
- });
320
- rstream2.on('end', function () {
321
- if (savestream) {
322
- savestream.done();
323
- }
324
- });
325
- rstream2.on('content-length', function (v) {
326
- readStream.emit('content-length', v);
375
+ const [updatedManifest] = await this.syncUplinksMetadataNext(name, cachedManifest, {
376
+ uplinksLook: true
377
+ });
378
+ const distFile = updatedManifest._distfiles[filename];
327
379
 
328
- if (savestream) {
329
- savestream.emit('content-length', v);
330
- }
331
- });
332
- rstream2.pipe(readStream);
380
+ if (updatedManifest === null || !distFile) {
381
+ debug('remote tarball not found');
382
+ throw _core.errorUtils.getNotFound(_core.API_ERROR.NO_SUCH_FILE);
383
+ }
333
384
 
334
- if (savestream) {
335
- rstream2.pipe(savestream);
385
+ const proxy = this.getUpLinkForDistFile(name, distFile);
386
+ const remoteStream = proxy.fetchTarballNext(distFile.url, {});
387
+ remoteStream.on('response', async () => {
388
+ try {
389
+ const storage = this.getPrivatePackageStorage(name);
390
+
391
+ if (proxy.config.cache === true && storage) {
392
+ debug('cache remote tarball enabled');
393
+ const localStorageWriteStream = await storage.writeTarball(filename, {
394
+ signal
395
+ });
396
+ await (0, _promises.pipeline)(remoteStream, passThroughRemoteStream, localStorageWriteStream, {
397
+ signal
398
+ });
399
+ } else {
400
+ debug('cache remote tarball disabled');
401
+ await (0, _promises.pipeline)(remoteStream, passThroughRemoteStream, {
402
+ signal
403
+ });
404
+ }
405
+ } catch (err) {
406
+ debug('error on pipeline downloading tarball for package %o', name);
407
+ passThroughRemoteStream.emit('error', err);
336
408
  }
337
- };
409
+ });
410
+ return passThroughRemoteStream;
411
+ }
412
+ }
413
+ /**
414
+ *
415
+ * @param name
416
+ * @param filename
417
+ * @param param2
418
+ * @returns
419
+ */
338
420
 
339
- if (savestream) {
340
- savestream.on('open', function () {
341
- on_open();
342
- });
343
- savestream.on('error', function (err) {
344
- self.logger.warn({
345
- err: err,
346
- fileName: file
347
- }, 'error saving file @{fileName}: @{err?.message}\n@{err.stack}');
348
-
349
- if (savestream) {
350
- savestream.abort();
351
- }
352
421
 
353
- savestream = null;
354
- on_open();
422
+ async getTarballNext(name, filename, {
423
+ signal
424
+ }) {
425
+ debug('get tarball for package %o filename %o', name, filename); // TODO: check if isOpen is need it after all.
426
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
427
+
428
+ let isOpen = false;
429
+ const localTarballStream = new _stream.PassThrough();
430
+ const localStream = await this.getLocalTarball(name, filename, {
431
+ signal
432
+ });
433
+ localStream.on('open', async () => {
434
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
435
+ isOpen = true;
436
+ await (0, _promises.pipeline)(localStream, localTarballStream, {
437
+ signal
438
+ });
439
+ });
440
+ localStream.on('error', err => {
441
+ // eslint-disable-next-line no-console
442
+ if (err.code === _storageUtils.STORAGE.NO_SUCH_FILE_ERROR || err.code === _core.HTTP_STATUS.NOT_FOUND) {
443
+ this.getTarballFromUpstream(name, filename, {
444
+ signal
445
+ }).then(uplinkStream => {
446
+ (0, _promises.pipeline)(uplinkStream, localTarballStream, {
447
+ signal
448
+ }).then(() => {
449
+ debug('successfully downloaded tarball for package %o filename %o', name, filename);
450
+ }).catch(err => {
451
+ localTarballStream.emit('error', err);
452
+ });
453
+ }).catch(err => {
454
+ localTarballStream.emit('error', err);
355
455
  });
356
456
  } else {
357
- on_open();
358
- }
359
- }
360
- } // public async starPackage(body: StarBody, options: IGetPackageOptionsNext): Promise<void> {
361
- // debug('star package');
362
- // const manifest = await this.getPackageNext(options);
363
- // const newStarUser = body[constants.USERS];
364
- // const remoteUsername: string = options.remoteUser.name as string;
365
- // const localStarUsers = manifest[constants.USERS];
366
- // // Check is star or unstar
367
- // const isStar = Object.keys(newStarUser).includes(remoteUsername);
368
- // debug('is start? %o', isStar);
369
- // if (
370
- // _.isNil(localStarUsers) === false &&
371
- // validateInputs(localStarUsers, remoteUsername, isStar)
372
- // ) {
373
- // // return afterChangePackage();
374
- // }
375
- // const users: Users = isStar
376
- // ? {
377
- // ...localStarUsers,
378
- // [remoteUsername]: true,
379
- // }
380
- // : _.reduce(
381
- // localStarUsers,
382
- // (users, value, key) => {
383
- // if (key !== remoteUsername) {
384
- // users[key] = value;
385
- // }
386
- // return users;
387
- // },
388
- // {}
389
- // );
390
- // debug('update package for %o', name);
391
- // }
392
- // public async publish(body: any, options: IGetPackageOptionsNext): Promise<any> {
393
- // const { name } = options;
394
- // debug('publishing or updating a new version for %o', name);
395
- // // we check if the request is npm star
396
- // if (!isPublishablePackage(body) && isObject(body.users)) {
397
- // debug('starting star a package');
398
- // await this.starPackage(body as StarBody, options);
399
- // }
400
- // return { ok: API_MESSAGE.PKG_CHANGED, success: true };
401
- // }
402
-
457
+ this.logger.error({
458
+ err: err.message
459
+ }, 'some error on fatal @{err}');
460
+ localTarballStream.emit('error', err);
461
+ }
462
+ });
463
+ return localTarballStream;
464
+ }
403
465
 
404
466
  async getPackageByVersion(options) {
405
467
  const queryVersion = options.version;
@@ -410,7 +472,7 @@ class Storage {
410
472
 
411
473
 
412
474
  const [convertedManifest] = await this.getPackageNext(options);
413
- const version = (0, _utils.getVersion)(convertedManifest.versions, queryVersion);
475
+ const version = (0, _versionsUtils.getVersion)(convertedManifest.versions, queryVersion);
414
476
  debug('query by latest version %o and result %o', queryVersion, version);
415
477
 
416
478
  if (typeof version !== 'undefined') {
@@ -425,7 +487,7 @@ class Storage {
425
487
  // the version found as a distag
426
488
  const matchedDisTagVersion = convertedManifest[_core.DIST_TAGS][queryVersion];
427
489
  debug('dist-tag version found %o', matchedDisTagVersion);
428
- const disTagVersion = (0, _utils.getVersion)(convertedManifest.versions, matchedDisTagVersion);
490
+ const disTagVersion = (0, _versionsUtils.getVersion)(convertedManifest.versions, matchedDisTagVersion);
429
491
 
430
492
  if (typeof disTagVersion !== 'undefined') {
431
493
  debug('dist-tag found %o', disTagVersion);
@@ -447,6 +509,44 @@ class Storage {
447
509
  const convertedManifest = (0, _tarball.convertDistRemoteToLocalTarballUrls)(manifest, options.requestOptions, this.config.url_prefix);
448
510
  return convertedManifest;
449
511
  }
512
+
513
+ convertAbbreviatedManifest(manifest) {
514
+ const abbreviatedVersions = Object.keys(manifest.versions).reduce((acc, version) => {
515
+ const _version = manifest.versions[version]; // This should be align with this document
516
+ // https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-version-object
517
+
518
+ const _version_abbreviated = {
519
+ name: _version.name,
520
+ version: _version.version,
521
+ description: _version.description,
522
+ deprecated: _version.deprecated,
523
+ bin: _version.bin,
524
+ dist: _version.dist,
525
+ engines: _version.engines,
526
+ funding: _version.funding,
527
+ directories: _version.directories,
528
+ dependencies: _version.dependencies,
529
+ devDependencies: _version.devDependencies,
530
+ peerDependencies: _version.peerDependencies,
531
+ optionalDependencies: _version.optionalDependencies,
532
+ bundleDependencies: _version.bundleDependencies,
533
+ // npm cli specifics
534
+ _hasShrinkwrap: _version._hasShrinkwrap,
535
+ hasInstallScript: _version.hasInstallScript
536
+ };
537
+ acc[version] = _version_abbreviated;
538
+ return acc;
539
+ }, {});
540
+ const convertedManifest = {
541
+ name: manifest['name'],
542
+ [_core.DIST_TAGS]: manifest[_core.DIST_TAGS],
543
+ versions: abbreviatedVersions,
544
+ modified: manifest.time.modified,
545
+ // NOTE: special case for pnpm https://github.com/pnpm/rfcs/pull/2
546
+ time: manifest.time
547
+ };
548
+ return convertedManifest;
549
+ }
450
550
  /**
451
551
  * Return a manifest or version based on the options.
452
552
  * @param options {Object}
@@ -459,329 +559,1362 @@ class Storage {
459
559
  if (_lodash.default.isNil(options.version) === false) {
460
560
  return this.getPackageByVersion(options);
461
561
  } else {
462
- return this.getPackageManifest(options);
562
+ const manifest = await this.getPackageManifest(options);
563
+
564
+ if (options.abbreviated === true) {
565
+ debug('abbreviated manifest');
566
+ return this.convertAbbreviatedManifest(manifest);
567
+ }
568
+
569
+ return manifest;
463
570
  }
464
571
  }
465
572
 
466
- async getPackageNext(options) {
467
- const {
468
- name
469
- } = options;
470
- debug('get package for %o', name);
471
-
472
- try {
473
- let data;
573
+ async getLocalDatabaseNext() {
574
+ debug('get local database');
575
+ const storage = this.localStorage.getStoragePlugin();
576
+ const database = await storage.get();
577
+ const packages = [];
474
578
 
475
- try {
476
- data = await this.localStorage.getPackageMetadataNext(name);
477
- } catch (err) {
478
- // we don't have package locally, so we need to fetch it from uplinks
479
- if (err && (!err.status || err.status >= _core.HTTP_STATUS.INTERNAL_ERROR)) {
480
- throw err;
481
- }
482
- } // time to sync with uplinks if we have any
579
+ for (const pkg of database) {
580
+ debug('get local database %o', pkg);
581
+ const manifest = await this.getPackageLocalMetadata(pkg);
582
+ const latest = manifest[_core.DIST_TAGS].latest;
483
583
 
584
+ if (latest && manifest.versions[latest]) {
585
+ const version = manifest.versions[latest];
586
+ const timeList = manifest.time;
587
+ const time = timeList[latest]; // @ts-ignore
484
588
 
485
- debug('sync uplinks for %o', name); // @ts-expect-error
589
+ version.time = time; // Add for stars api
590
+ // @ts-ignore
486
591
 
487
- return this._syncUplinksMetadataNext(name, data, {
488
- req: options.req,
489
- uplinksLook: options.uplinksLook,
490
- keepUpLinkData: options.keepUpLinkData
491
- });
492
- } catch (err) {
493
- this.logger.error({
494
- name,
495
- err: err.message
496
- }, 'error on get package for @{name} with error @{err}');
497
- throw err;
592
+ version.users = manifest.users;
593
+ packages.push(version);
594
+ } else {
595
+ this.logger.warn({
596
+ package: pkg
597
+ }, 'package @{package} does not have a "latest" tag?');
598
+ }
498
599
  }
499
- }
500
-
501
- _syncUplinksMetadataNext(name, data, {
502
- uplinksLook,
503
- keepUpLinkData,
504
- req
505
- }) {
506
- return new Promise((resolve, reject) => {
507
- this._syncUplinksMetadata(name, data, {
508
- req: req,
509
- uplinksLook
510
- }, function getPackageSynUpLinksCallback(err, result, uplinkErrors) {
511
- if (err) {
512
- debug('error on sync package for %o with error %o', name, err === null || err === void 0 ? void 0 : err.message);
513
- return reject(err);
514
- }
515
600
 
516
- const normalizedPkg = Object.assign({}, result, { ...(0, _utils.normalizeDistTags)((0, _storageUtils.cleanUpLinksRef)(result, keepUpLinkData)),
517
- _attachments: {}
518
- });
519
- debug('no. sync uplinks errors %o for %s', uplinkErrors === null || uplinkErrors === void 0 ? void 0 : uplinkErrors.length, name);
520
- resolve([normalizedPkg, uplinkErrors]);
521
- });
522
- });
601
+ return packages;
523
602
  }
524
- /**
525
- Retrieve a package metadata for {name} package
526
- Function invokes localStorage.getPackage and uplink.get_package for every
527
- uplink with proxy_access rights against {name} and combines results
528
- into one json object
529
- Used storages: local && uplink (proxy_access)
530
- * @param {object} options
531
- * @property {string} options.name Package Name
532
- * @property {object} options.req Express `req` object
533
- * @property {boolean} options.keepUpLinkData keep up link info in package meta, last update, etc.
534
- * @property {function} options.callback Callback for receive data
535
- */
536
603
 
604
+ saveToken(token) {
605
+ if (_lodash.default.isFunction(this.localStorage.getStoragePlugin().saveToken) === false) {
606
+ return Promise.reject(_core.errorUtils.getCode(_core.HTTP_STATUS.SERVICE_UNAVAILABLE, _core.SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE));
607
+ }
537
608
 
538
- getPackage(options) {
539
- const {
540
- name
541
- } = options;
542
- debug('get package for %o', name);
543
- this.localStorage.getPackageMetadata(name, (err, data) => {
544
- if (err && (!err.status || err.status >= _core.HTTP_STATUS.INTERNAL_ERROR)) {
545
- // report internal errors right away
546
- debug('error on get package for %o with error %o', name, err === null || err === void 0 ? void 0 : err.message);
547
- return options.callback(err);
548
- }
609
+ return this.localStorage.getStoragePlugin().saveToken(token);
610
+ }
549
611
 
550
- debug('sync uplinks for %o', name);
612
+ deleteToken(user, tokenKey) {
613
+ if (_lodash.default.isFunction(this.localStorage.getStoragePlugin().deleteToken) === false) {
614
+ return Promise.reject(_core.errorUtils.getCode(_core.HTTP_STATUS.SERVICE_UNAVAILABLE, _core.SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE));
615
+ }
551
616
 
552
- this._syncUplinksMetadata(name, data, {
553
- req: options.req,
554
- uplinksLook: options.uplinksLook
555
- }, function getPackageSynUpLinksCallback(err, result, uplinkErrors) {
556
- if (err) {
557
- debug('error on sync package for %o with error %o', name, err === null || err === void 0 ? void 0 : err.message);
558
- return options.callback(err);
559
- }
617
+ return this.localStorage.getStoragePlugin().deleteToken(user, tokenKey);
618
+ }
560
619
 
561
- result = (0, _utils.normalizeDistTags)((0, _storageUtils.cleanUpLinksRef)(result, options === null || options === void 0 ? void 0 : options.keepUpLinkData)); // npm can throw if this field doesn't exist
620
+ readTokens(filter) {
621
+ if (_lodash.default.isFunction(this.localStorage.getStoragePlugin().readTokens) === false) {
622
+ return Promise.reject(_core.errorUtils.getCode(_core.HTTP_STATUS.SERVICE_UNAVAILABLE, _core.SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE));
623
+ }
562
624
 
563
- result._attachments = {};
564
- debug('no. sync uplinks errors %o', uplinkErrors === null || uplinkErrors === void 0 ? void 0 : uplinkErrors.length);
565
- options.callback(null, result, uplinkErrors);
566
- });
567
- });
625
+ return this.localStorage.getStoragePlugin().readTokens(filter);
568
626
  }
569
627
  /**
570
- * Retrieve only private local packages
571
- * @param {*} callback
628
+ * Initialize the storage asynchronously.
629
+ * @param config Config
630
+ * @param filters IPluginFilters
631
+ * @returns Storage instance
572
632
  */
573
633
 
574
634
 
575
- getLocalDatabase(callback) {
576
- const self = this;
577
- debug('get local database');
578
-
579
- if (this.localStorage.storagePlugin !== null) {
580
- this.localStorage.storagePlugin.get().then(locals => {
581
- const packages = [];
582
-
583
- const getPackage = function (itemPkg) {
584
- self.localStorage.getPackageMetadata(locals[itemPkg], function (err, pkgMetadata) {
585
- if (_lodash.default.isNil(err)) {
586
- const latest = pkgMetadata[_core.DIST_TAGS].latest;
587
-
588
- if (latest && pkgMetadata.versions[latest]) {
589
- const version = pkgMetadata.versions[latest];
590
- const timeList = pkgMetadata.time;
591
- const time = timeList[latest]; // @ts-ignore
592
-
593
- version.time = time; // Add for stars api
594
- // @ts-ignore
595
-
596
- version.users = pkgMetadata.users;
597
- packages.push(version);
598
- } else {
599
- self.logger.warn({
600
- package: locals[itemPkg]
601
- }, 'package @{package} does not have a "latest" tag?');
602
- }
603
- }
604
-
605
- if (itemPkg >= locals.length - 1) {
606
- callback(null, packages);
607
- } else {
608
- getPackage(itemPkg + 1);
609
- }
610
- });
611
- };
635
+ async init(config, filters = []) {
636
+ if (this.localStorage === null) {
637
+ this.filters = filters || [];
638
+ debug('filters available %o', filters);
639
+ this.localStorage = new _localStorage.LocalStorage(this.config, _logger.logger);
640
+ await this.localStorage.init();
641
+ debug('local init storage initialized');
642
+ await this.localStorage.getSecret(config);
643
+ debug('local storage secret initialized');
644
+ } else {
645
+ debug('storage has been already initialized');
646
+ }
612
647
 
613
- if (locals.length) {
614
- getPackage(0);
615
- } else {
616
- callback(null, []);
617
- }
618
- }).catch(err => {
619
- callback(err);
648
+ return;
649
+ }
650
+ /**
651
+ * Consume the upstream and pipe it to a transformable stream.
652
+ */
653
+
654
+
655
+ consumeSearchStream(uplinkId, uplink, options, searchPassThrough) {
656
+ return uplink.search({ ...options
657
+ }).then(bodyStream => {
658
+ bodyStream.pipe(searchPassThrough, {
659
+ end: false
620
660
  });
661
+ bodyStream.on('error', err => {
662
+ _logger.logger.error({
663
+ uplinkId,
664
+ err: err
665
+ }, 'search error for uplink @{uplinkId}: @{err?.message}');
666
+
667
+ searchPassThrough.end();
668
+ });
669
+ return new Promise(resolve => bodyStream.on('end', resolve));
670
+ });
671
+ }
672
+ /**
673
+ * Retrieve a wrapper that provide access to the package location.
674
+ * @param {Object} pkgName package name.
675
+ * @return {Object}
676
+ */
677
+
678
+
679
+ getPrivatePackageStorage(pkgName) {
680
+ debug('get local storage for %o', pkgName);
681
+ return this.localStorage.getStoragePlugin().getPackageStorage(pkgName);
682
+ }
683
+ /**
684
+ * Create a tarball stream from a package.
685
+ * @param name
686
+ * @param filename
687
+ * @param options
688
+ * @returns
689
+ */
690
+
691
+
692
+ async getLocalTarball(pkgName, filename, {
693
+ signal
694
+ }) {
695
+ (0, _assert.default)(_core.validatioUtils.validateName(filename));
696
+ const storage = this.getPrivatePackageStorage(pkgName);
697
+
698
+ if (typeof storage === 'undefined') {
699
+ return this.createFailureStreamResponseNext();
700
+ }
701
+
702
+ return await storage.readTarball(filename, {
703
+ signal
704
+ });
705
+ }
706
+
707
+ async searchCachedPackages(searchStream, query) {
708
+ debug('search on each package');
709
+ this.logger.info({
710
+ t: query.text,
711
+ q: query.quality,
712
+ p: query.popularity,
713
+ m: query.maintenance,
714
+ s: query.size
715
+ }, 'search by text @{t}| maintenance @{m}| quality @{q}| popularity @{p}');
716
+
717
+ if (typeof this.localStorage.getStoragePlugin().search === 'undefined') {
718
+ this.logger.info('plugin search not implemented yet');
719
+ searchStream.end();
621
720
  } else {
622
- debug('local stora instance is null');
721
+ debug('search on each package by plugin');
722
+ const items = await this.localStorage.getStoragePlugin().search(query);
723
+
724
+ try {
725
+ for (const searchItem of items) {
726
+ const manifest = await this.getPackageLocalMetadata(searchItem.package.name);
727
+
728
+ if (_lodash.default.isEmpty(manifest === null || manifest === void 0 ? void 0 : manifest.versions) === false) {
729
+ const searchPackage = (0, _2.mapManifestToSearchPackageBody)(manifest, searchItem);
730
+ const searchPackageItem = {
731
+ package: searchPackage,
732
+ score: searchItem.score,
733
+ verdaccioPkgCached: searchItem.verdaccioPkgCached,
734
+ verdaccioPrivate: searchItem.verdaccioPrivate,
735
+ flags: searchItem === null || searchItem === void 0 ? void 0 : searchItem.flags,
736
+ // FUTURE: find a better way to calculate the score
737
+ searchScore: 1
738
+ };
739
+ searchStream.write(searchPackageItem);
740
+ }
741
+ }
742
+
743
+ debug('search local stream end');
744
+ searchStream.end();
745
+ } catch (err) {
746
+ this.logger.error({
747
+ err,
748
+ query
749
+ }, 'error on search by plugin @{err.message}');
750
+ searchStream.emit('error', err);
751
+ }
752
+ }
753
+ }
754
+
755
+ async removePackageByRevision(pkgName, revision) {
756
+ const storage = this.getPrivatePackageStorage(pkgName);
757
+ debug('get package metadata for %o', pkgName);
758
+
759
+ if (typeof storage === 'undefined') {
760
+ throw _core.errorUtils.getServiceUnavailable('storage not initialized');
623
761
  }
624
- } // notas, debo migrar _syncUplinksMetadata algo mas lindo
625
762
 
763
+ let manifest;
764
+
765
+ try {
766
+ manifest = await storage.readPackage(pkgName);
767
+ manifest = (0, _storageUtils.normalizePackage)(manifest);
768
+ } catch (err) {
769
+ if (err.code === _storageUtils.STORAGE.NO_SUCH_FILE_ERROR || err.code === _core.HTTP_STATUS.NOT_FOUND) {
770
+ _logger.logger.info({
771
+ pkgName,
772
+ revision
773
+ }, 'package not found');
774
+
775
+ throw _core.errorUtils.getNotFound();
776
+ }
777
+
778
+ _logger.logger.error({
779
+ pkgName,
780
+ revision,
781
+ err: err.message
782
+ }, 'error @{err} while reading package @{pkgName}-{revision}');
783
+
784
+ throw err;
785
+ } // TODO: move this to another method
786
+
787
+
788
+ try {
789
+ await this.localStorage.getStoragePlugin().remove(pkgName); // remove each attachment
790
+
791
+ const attachments = Object.keys(manifest._attachments);
792
+ debug('attachments to remove %s', attachments === null || attachments === void 0 ? void 0 : attachments.length);
793
+
794
+ for (let attachment of attachments) {
795
+ debug('remove attachment %s', attachment);
796
+ await storage.deletePackage(attachment);
797
+
798
+ _logger.logger.info({
799
+ attachment
800
+ }, 'attachment @{attachment} removed');
801
+ } // remove package.json
802
+
803
+
804
+ debug('remove package.json');
805
+ await storage.deletePackage(_storageUtils.STORAGE.PACKAGE_FILE_NAME); // remove folder
806
+
807
+ debug('remove package folder');
808
+ await storage.removePackage();
809
+
810
+ _logger.logger.info({
811
+ pkgName
812
+ }, 'package @{pkgName} removed');
813
+ } catch (err) {
814
+ this.logger.error({
815
+ err
816
+ }, 'removed package has failed @{err.message}');
817
+ throw _core.errorUtils.getBadData(err.message);
818
+ }
819
+ }
626
820
  /**
627
- * Function fetches package metadata from uplinks and synchronizes it with local data
628
- if package is available locally, it MUST be provided in pkginfo
629
- returns callback(err, result, uplink_errors)
821
+ * Get a package local manifest.
822
+ *
823
+ * Fails if package is not found.
824
+ * @param name package name
825
+ * @param revision of package
826
+ * @returns local manifest
630
827
  */
828
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
631
829
 
632
830
 
633
- _syncUplinksMetadata(name, packageInfo, options, callback) {
634
- let found = true;
635
- const self = this;
636
- const upLinks = [];
637
- const hasToLookIntoUplinks = _lodash.default.isNil(options.uplinksLook) || options.uplinksLook;
638
- debug('is sync uplink enabled %o', hasToLookIntoUplinks);
831
+ async getPackageLocalMetadata(name, _revision) {
832
+ const storage = this.getPrivatePackageStorage(name);
833
+ debug('get package metadata for %o', name);
639
834
 
640
- if (!packageInfo) {
641
- found = false;
642
- packageInfo = (0, _storageUtils.generatePackageTemplate)(name);
835
+ if (typeof storage === 'undefined') {
836
+ throw _core.errorUtils.getServiceUnavailable('storage not initialized');
643
837
  }
644
838
 
645
- for (const uplink in this.uplinks) {
646
- if ((0, _config.hasProxyTo)(name, uplink, this.config.packages) && hasToLookIntoUplinks) {
647
- upLinks.push(this.uplinks[uplink]);
839
+ try {
840
+ const result = await storage.readPackage(name);
841
+ return (0, _storageUtils.normalizePackage)(result);
842
+ } catch (err) {
843
+ if (err.code === _storageUtils.STORAGE.NO_SUCH_FILE_ERROR || err.code === _core.HTTP_STATUS.NOT_FOUND) {
844
+ debug('package %s not found', name);
845
+ throw _core.errorUtils.getNotFound();
648
846
  }
847
+
848
+ this.logger.error({
849
+ err: err,
850
+ file: _storageUtils.STORAGE.PACKAGE_FILE_NAME
851
+ }, `error reading @{file}: @{!err.message}`);
852
+ throw _core.errorUtils.getInternalError();
649
853
  }
854
+ }
855
+ /**
856
+ * Fail the stream response with an not found error.
857
+ * @returns
858
+ */
650
859
 
651
- debug('uplink list %o', upLinks.length);
652
860
 
653
- _async.default.map(upLinks, (upLink, cb) => {
654
- const _options = Object.assign({}, options);
861
+ createFailureStreamResponseNext() {
862
+ const stream = new _stream.PassThrough(); // we ensure fails on the next tick into the event loop
655
863
 
656
- const upLinkMeta = packageInfo._uplinks[upLink.upname];
864
+ process.nextTick(() => {
865
+ stream.emit('error', _core.errorUtils.getNotFound(_core.API_ERROR.NO_SUCH_FILE));
866
+ });
867
+ return stream;
868
+ }
869
+ /**
870
+ * Update a package and merge tags
871
+ * @param name package name
872
+ * @param tags list of dist-tags
873
+ */
657
874
 
658
- if (_core.validatioUtils.isObject(upLinkMeta)) {
659
- const fetched = upLinkMeta.fetched;
660
875
 
661
- if (fetched && Date.now() - fetched < upLink.maxage) {
662
- return cb();
876
+ async mergeTagsNext(name, tags) {
877
+ return await this.updatePackageNext(name, async data => {
878
+ let newData = { ...data
879
+ };
880
+
881
+ for (const tag of Object.keys(tags)) {
882
+ // this handle dist-tag rm command
883
+ if (_lodash.default.isNull(tags[tag])) {
884
+ delete newData[_core.DIST_TAGS][tag];
885
+ continue;
886
+ }
887
+
888
+ if (_lodash.default.isNil(newData.versions[tags[tag]])) {
889
+ throw _core.errorUtils.getNotFound(_core.API_ERROR.VERSION_NOT_EXIST);
663
890
  }
664
891
 
665
- _options.etag = upLinkMeta.etag;
892
+ const version = tags[tag];
893
+ newData = (0, _2.tagVersionNext)(newData, version, tag);
666
894
  }
667
895
 
668
- upLink.getRemoteMetadata(name, _options, (err, upLinkResponse, eTag) => {
669
- if (err && err.remoteStatus === 304) {
670
- upLinkMeta.fetched = Date.now();
671
- }
896
+ return newData;
897
+ });
898
+ }
672
899
 
673
- if (err || !upLinkResponse) {
674
- return cb(null, [err || _core.errorUtils.getInternalError('no data')]);
675
- }
900
+ getUpLinkForDistFile(pkgName, distFile) {
901
+ let uplink = null;
902
+
903
+ for (const uplinkId in this.uplinks) {
904
+ // refer to https://github.com/verdaccio/verdaccio/issues/1642
905
+ if ((0, _config.hasProxyTo)(pkgName, uplinkId, this.config.packages)) {
906
+ uplink = this.uplinks[uplinkId];
907
+ }
908
+ }
909
+
910
+ if (uplink == null) {
911
+ debug('upstream not found creating one for %o', pkgName);
912
+ uplink = new _proxy.ProxyStorage({
913
+ url: distFile.url,
914
+ cache: true
915
+ }, this.config);
916
+ }
917
+
918
+ return uplink;
919
+ }
920
+
921
+ async updateLocalMetadata(pkgName) {
922
+ const storage = this.getPrivatePackageStorage(pkgName);
923
+
924
+ if (!storage) {
925
+ throw _core.errorUtils.getNotFound();
926
+ }
927
+ }
928
+
929
+ async updateManifest(manifest, options) {
930
+ if ((0, _2.isDeprecatedManifest)(manifest)) {
931
+ // if the manifest is deprecated, we need to update the package.json
932
+ await this.deprecate(manifest, { ...options
933
+ });
934
+ } else if ((0, _starUtils.isPublishablePackage)(manifest) === false && _core.validatioUtils.isObject(manifest.users)) {
935
+ // if user request to apply a star to the manifest
936
+ await this.star(manifest, { ...options
937
+ });
938
+ } else if (_core.validatioUtils.validatePublishSingleVersion(manifest)) {
939
+ // if continue, the version to be published does not exist
940
+ // we create a new package
941
+ const [mergedManifest, version] = await this.publishANewVersion(manifest, { ...options
942
+ }); // send notification of publication (notification step, non transactional)
943
+
944
+ try {
945
+ const {
946
+ name
947
+ } = mergedManifest;
948
+ await this.notify(mergedManifest, `${name}@${version}`);
949
+
950
+ _logger.logger.info({
951
+ name,
952
+ version
953
+ }, 'notify for @{name}@@{version} has been sent');
954
+ } catch (error) {
955
+ _logger.logger.error({
956
+ error: error.message
957
+ }, 'notify batch service has failed: @{error}');
958
+ }
959
+ } else {
960
+ debug('invalid body format');
961
+
962
+ _logger.logger.info({
963
+ packageName: options.name
964
+ }, `wrong package format on publish a package @{packageName}`);
965
+
966
+ throw _core.errorUtils.getBadRequest(_core.API_ERROR.UNSUPORTED_REGISTRY_CALL);
967
+ }
968
+ } // eslint-disable-next-line @typescript-eslint/no-unused-vars
969
+
970
+
971
+ async deprecate(_body, _options) {
972
+ // // const storage: IPackageStorage = this.getPrivatePackageStorage(opname);
973
+ // if (typeof storage === 'undefined') {
974
+ // throw errorUtils.getNotFound();
975
+ // }
976
+ throw _core.errorUtils.getInternalError('no implemenation ready for npm deprecate');
977
+ } // eslint-disable-next-line @typescript-eslint/no-unused-vars
978
+
979
+
980
+ async star(_body, _options) {
981
+ // // const storage: IPackageStorage = this.getPrivatePackageStorage(opname);
982
+ // if (typeof storage === 'undefined') {
983
+ // throw errorUtils.getNotFound();
984
+ // }
985
+ throw _core.errorUtils.getInternalError('no implemenation ready for npm star');
986
+ }
987
+ /**
988
+ * Get local package, on fails return null.
989
+ * Errors are considered package not found.
990
+ * @param name
991
+ * @returns
992
+ */
993
+
994
+
995
+ async getPackagelocalByNameNext(name) {
996
+ try {
997
+ return await this.getPackageLocalMetadata(name);
998
+ } catch (err) {
999
+ debug('local package %s not found', name);
1000
+ return null;
1001
+ }
1002
+ }
1003
+ /**
1004
+ * Convert tarball as string into a Buffer and validate the length.
1005
+ * @param data the tarball data as string
1006
+ * @returns
1007
+ */
1008
+
1009
+
1010
+ getBufferManifest(data) {
1011
+ const buffer = Buffer.from(data, 'base64');
1012
+
1013
+ if (buffer.length === 0) {
1014
+ throw _core.errorUtils.getBadData('refusing to accept zero-length file');
1015
+ }
1016
+
1017
+ return buffer;
1018
+ }
1019
+ /**
1020
+ * Verify if the package exists in the local storage
1021
+ * (the package refers to the package.json), directory would return false.
1022
+ * @param pkgName package name
1023
+ * @returns boolean
1024
+ */
1025
+
1026
+
1027
+ async hasPackage(pkgName) {
1028
+ const storage = this.getPrivatePackageStorage(pkgName);
1029
+
1030
+ if (typeof storage === 'undefined') {
1031
+ throw _core.errorUtils.getNotFound();
1032
+ }
1033
+
1034
+ const hasPackage = await storage.hasPackage();
1035
+ debug('has package %o for %o', pkgName, hasPackage);
1036
+ return hasPackage;
1037
+ }
1038
+ /**
1039
+ * Create a new package.
1040
+ * This situation happens only of the package does not exist on the cache.
1041
+ *
1042
+ * @param body package metadata
1043
+ * @param options
1044
+ * @returns
1045
+ */
1046
+
1047
+
1048
+ async publishANewVersion(body, options) {
1049
+ const {
1050
+ name
1051
+ } = options;
1052
+ debug('publishing a new package for %o', name);
1053
+ const manifest = { ..._core.validatioUtils.normalizeMetadata(body, name)
1054
+ };
1055
+ const {
1056
+ _attachments,
1057
+ versions
1058
+ } = manifest; // validation step, if _attachments is not an object throw error
1059
+ // _attachments is need it for holding the tarball buffer in the local storage as file
1060
+ // versions is need it for holding the version in the local storage as file
1061
+ // _attachments and validation are required otherwise cannot continue.
1062
+
1063
+ if ((0, _lodash.isEmpty)(_attachments)) {
1064
+ throw _core.errorUtils.getBadRequest(_core.API_ERROR.UNSUPORTED_REGISTRY_CALL);
1065
+ } // get the unique version available
1066
+
1067
+
1068
+ const [versionToPublish] = Object.keys(versions); // at this point document is either created or existed before
1069
+
1070
+ const [firstAttachmentKey] = Object.keys(_attachments);
1071
+ const buffer = this.getBufferManifest(body._attachments[firstAttachmentKey].data);
1072
+
1073
+ try {
1074
+ // we check if package exist already locally
1075
+ const manifest = await this.getPackagelocalByNameNext(name); // if continue, the version to be published does not exist
1076
+
1077
+ if ((manifest === null || manifest === void 0 ? void 0 : manifest.versions[versionToPublish]) != null) {
1078
+ debug('%s version %s already exists', name, versionToPublish);
1079
+ throw _core.errorUtils.getConflict();
1080
+ } // if execution get here, package does not exist locally, we search upstream
1081
+
1082
+
1083
+ const remoteManifest = await this.checkPackageRemote(name, this.isAllowPublishOffline());
1084
+
1085
+ if ((remoteManifest === null || remoteManifest === void 0 ? void 0 : remoteManifest.versions[versionToPublish]) != null) {
1086
+ debug('%s version %s already exists', name, versionToPublish);
1087
+ throw _core.errorUtils.getConflict();
1088
+ }
1089
+
1090
+ const hasPackageInStorage = await this.hasPackage(name);
1091
+
1092
+ if (!hasPackageInStorage) {
1093
+ await this.createNewLocalCachePackage(name, versionToPublish);
1094
+ }
1095
+ } catch (err) {
1096
+ debug('error on change or update a package with %o', err.message);
1097
+
1098
+ _logger.logger.error({
1099
+ err: err.message
1100
+ }, 'error on create package: @{err}');
1101
+
1102
+ throw err;
1103
+ } // 1. after tarball has been successfully uploaded, we update the version
1104
+
1105
+
1106
+ try {
1107
+ // TODO: review why do this
1108
+ versions[versionToPublish].readme = _lodash.default.isNil(manifest.readme) === false ? String(manifest.readme) : '';
1109
+ await this.addVersionNext(name, versionToPublish, versions[versionToPublish], null);
1110
+ } catch (err) {
1111
+ _logger.logger.error({
1112
+ err: err.message
1113
+ }, 'updated version has failed: @{err}');
1114
+
1115
+ debug('error on create a version for %o with error %o', name, err.message); // TODO: remove tarball if add version fails
1116
+
1117
+ throw err;
1118
+ } // 2. update and merge tags
1119
+
1120
+
1121
+ let mergedManifest;
1122
+
1123
+ try {
1124
+ // note: I could merge this with addVersionNext
1125
+ // 1. add version
1126
+ // 2. merge versions
1127
+ // 3. upload tarball
1128
+ // 3.update once to the storage (easy peasy)
1129
+ mergedManifest = await this.mergeTagsNext(name, manifest[_core.DIST_TAGS]);
1130
+ } catch (err) {
1131
+ _logger.logger.error({
1132
+ err: err.message
1133
+ }, 'merge version has failed: @{err}');
1134
+
1135
+ debug('error on create a version for %o with error %o', name, err.message); // TODO: undo if this fails
1136
+ // 1. remove tarball
1137
+ // 2. remove updated version
1138
+
1139
+ throw err;
1140
+ } // 3. upload the tarball to the storage
1141
+
1142
+
1143
+ try {
1144
+ const readable = _stream.Readable.from(buffer);
1145
+
1146
+ await this.uploadTarball(name, firstAttachmentKey, readable, {
1147
+ signal: options.signal
1148
+ });
1149
+ } catch (err) {
1150
+ _logger.logger.error({
1151
+ err: err.message
1152
+ }, 'upload tarball has failed: @{err}');
1153
+
1154
+ throw err;
1155
+ }
1156
+
1157
+ _logger.logger.info({
1158
+ name,
1159
+ version: versionToPublish
1160
+ }, 'package @{name}@@{version} has been published');
1161
+
1162
+ return [mergedManifest, versionToPublish];
1163
+ } // TODO: pending implementation
1164
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1165
+
1166
+
1167
+ async notify(_manifest, _message) {
1168
+ return;
1169
+ }
1170
+
1171
+ getProxyList() {
1172
+ const uplinksList = Object.keys(this.uplinks);
1173
+ return uplinksList;
1174
+ }
1175
+ /**
1176
+ * Wrap uploadTarballAsStream into a promise.
1177
+ * @param name package name
1178
+ * @param fileName tarball name
1179
+ * @param contentReadable content as readable stream
1180
+ * @param options
1181
+ * @returns
1182
+ */
1183
+
1184
+
1185
+ async uploadTarball(name, fileName, contentReadable, {
1186
+ signal
1187
+ }) {
1188
+ return new Promise((resolve, reject) => {
1189
+ (async () => {
1190
+ const stream = await this.uploadTarballAsStream(name, fileName, {
1191
+ signal
1192
+ });
1193
+ stream.on('error', err => {
1194
+ debug('error on stream a tarball %o for %o with error %o', 'foo.tar.gz', name, err.message);
1195
+ reject(err);
1196
+ });
1197
+ stream.on('success', () => {
1198
+ this.logger.debug({
1199
+ fileName,
1200
+ name
1201
+ }, 'file @{fileName} for package @{name} has been succesfully uploaded');
1202
+ resolve();
1203
+ });
1204
+ await (0, _promises.pipeline)(contentReadable, stream, {
1205
+ signal
1206
+ });
1207
+ })().catch(err => {
1208
+ reject(err);
1209
+ });
1210
+ });
1211
+ }
1212
+
1213
+ async uploadTarballAsStream(pkgName, filename, {
1214
+ signal
1215
+ }) {
1216
+ debug(`add a tarball for %o`, pkgName);
1217
+ (0, _assert.default)(_core.validatioUtils.validateName(filename));
1218
+ const shaOneHash = (0, _utils.createTarballHash)();
1219
+ const transformHash = new _stream.Transform({
1220
+ transform(chunk, _encoding, callback) {
1221
+ // measure the length for validation reasons
1222
+ shaOneHash.update(chunk);
1223
+ callback(null, chunk);
1224
+ }
1225
+
1226
+ });
1227
+ const uploadStream = new _stream.PassThrough();
1228
+ const storage = this.getPrivatePackageStorage(pkgName); // FUTURE: this validation could happen even before
1229
+
1230
+ if (pkgName === PROTO_NAME) {
1231
+ process.nextTick(() => {
1232
+ uploadStream.emit('error', _core.errorUtils.getForbidden());
1233
+ });
1234
+ return uploadStream;
1235
+ } // FIXME: this condition will never met, storage is always defined
1236
+
1237
+
1238
+ if (!storage) {
1239
+ process.nextTick(() => {
1240
+ uploadStream.emit('error', "can't upload this package storage is missing");
1241
+ });
1242
+ return uploadStream;
1243
+ }
1244
+
1245
+ const fileDoesExist = await storage.hasTarball(filename);
1246
+
1247
+ if (fileDoesExist) {
1248
+ process.nextTick(() => {
1249
+ uploadStream.emit('error', _core.errorUtils.getConflict());
1250
+ });
1251
+ } else {
1252
+ const localStorageWriteStream = await storage.writeTarball(filename, {
1253
+ signal
1254
+ });
1255
+ localStorageWriteStream.on('open', async () => {
1256
+ await (0, _promises.pipeline)(uploadStream, transformHash, localStorageWriteStream, {
1257
+ signal
1258
+ });
1259
+ }); // once the file descriptor has been closed
676
1260
 
1261
+ localStorageWriteStream.on('close', async () => {
677
1262
  try {
678
- _core.validatioUtils.validateMetadata(upLinkResponse, name);
1263
+ debug('uploaded tarball %o for %o', filename, pkgName); // update the package metadata
1264
+
1265
+ await this.updatePackageNext(pkgName, async data => {
1266
+ const newData = { ...data
1267
+ };
1268
+ debug('added _attachment for %o', pkgName);
1269
+ newData._attachments[filename] = {
1270
+ // TODO: add integrity hash here
1271
+ shasum: shaOneHash.digest('hex')
1272
+ };
1273
+ return newData;
1274
+ });
1275
+ debug('emit success for %o', pkgName);
1276
+ uploadStream.emit('success');
679
1277
  } catch (err) {
680
- self.logger.error({
681
- sub: 'out',
682
- err: err
683
- }, 'package.json validating error @{!err?.message}\n@{err.stack}');
684
- return cb(null, [err]);
1278
+ // FUTURE: if the update package fails, remove tarball to avoid left
1279
+ // orphan tarballs
1280
+ debug('something has failed on upload tarball %o for %o : %s', filename, pkgName, err.message);
1281
+ uploadStream.emit('error', err);
685
1282
  }
1283
+ }); // something went wrong writing into the local storage
686
1284
 
687
- packageInfo._uplinks[upLink.upname] = {
688
- etag: eTag,
689
- fetched: Date.now()
690
- };
691
- packageInfo.time = (0, _storageUtils.mergeUplinkTimeIntoLocal)(packageInfo, upLinkResponse);
692
- (0, _uplinkUtil.updateVersionsHiddenUpLink)(upLinkResponse.versions, upLink);
1285
+ localStorageWriteStream.on('error', async err => {
1286
+ uploadStream.emit('error', err);
1287
+ });
1288
+ }
693
1289
 
694
- try {
695
- _core.pkgUtils.mergeVersions(packageInfo, upLinkResponse);
696
- } catch (err) {
697
- self.logger.error({
698
- sub: 'out',
699
- err: err
700
- }, 'package.json parsing error @{!err?.message}\n@{err.stack}');
701
- return cb(null, [err]);
702
- } // if we got to this point, assume that the correct package exists
703
- // on the uplink
1290
+ return uploadStream;
1291
+ }
1292
+ /**
1293
+ * Add a new version to a package.
1294
+ * @param name package name
1295
+ * @param version version
1296
+ * @param metadata version metadata
1297
+ * @param tag tag of the version
1298
+ */
704
1299
 
705
1300
 
706
- found = true;
707
- cb();
708
- });
709
- }, // @ts-ignore
710
- (err, upLinksErrors) => {
711
- (0, _assert.default)(!err && Array.isArray(upLinksErrors)); // Check for connection timeout or reset errors with uplink(s)
712
- // (these should be handled differently from the package not being found)
713
-
714
- if (!found) {
715
- let uplinkTimeoutError;
716
-
717
- for (let i = 0; i < upLinksErrors.length; i++) {
718
- if (upLinksErrors[i]) {
719
- for (let j = 0; j < upLinksErrors[i].length; j++) {
720
- if (upLinksErrors[i][j]) {
721
- const code = upLinksErrors[i][j].code;
722
-
723
- if (code === 'ETIMEDOUT' || code === 'ESOCKETTIMEDOUT' || code === 'ECONNRESET') {
724
- uplinkTimeoutError = true;
725
- break;
726
- }
727
- }
1301
+ async addVersionNext(name, version, metadata, tag) {
1302
+ debug(`add version %s package for %s`, version, name);
1303
+ await this.updatePackageNext(name, async data => {
1304
+ // keep only one readme per package
1305
+ data.readme = metadata.readme;
1306
+ debug('%s` readme mutated', name); // TODO: lodash remove
1307
+
1308
+ metadata = (0, _2.cleanUpReadme)(metadata);
1309
+ metadata.contributors = (0, _utils.normalizeContributors)(metadata.contributors);
1310
+ debug('%s` contributors normalized', name); // if uploaded tarball has a different shasum, it's very likely that we
1311
+ // have some kind of error
1312
+
1313
+ if (_core.validatioUtils.isObject(metadata.dist) && _lodash.default.isString(metadata.dist.tarball)) {
1314
+ var _data$time;
1315
+
1316
+ const tarball = metadata.dist.tarball.replace(/.*\//, '');
1317
+
1318
+ if (_core.validatioUtils.isObject(data._attachments[tarball])) {
1319
+ if (_lodash.default.isNil(data._attachments[tarball].shasum) === false && _lodash.default.isNil(metadata.dist.shasum) === false) {
1320
+ if (data._attachments[tarball].shasum != metadata.dist.shasum) {
1321
+ const errorMessage = `shasum error, ` + `${data._attachments[tarball].shasum} != ${metadata.dist.shasum}`;
1322
+ throw _core.errorUtils.getBadRequest(errorMessage);
728
1323
  }
729
1324
  }
1325
+
1326
+ data._attachments[tarball].version = version;
1327
+ } // if the time field doesn't exist, we create it, some old storage
1328
+ // might not have it
1329
+ // https://github.com/verdaccio/verdaccio/issues/740
1330
+
1331
+
1332
+ if (_lodash.default.isNil(data.time)) {
1333
+ this.logger.warn({
1334
+ name
1335
+ }, 'time field could not be found, it has been recreated for @{name}');
1336
+ data.time = {};
1337
+ } // populate the time field with the date of the version
1338
+
1339
+
1340
+ const currentDate = new Date().toISOString();
1341
+ data.time.modified = currentDate;
1342
+
1343
+ if (!((_data$time = data.time) !== null && _data$time !== void 0 && _data$time.created)) {
1344
+ data.time.created = currentDate;
730
1345
  }
731
1346
 
732
- if (uplinkTimeoutError) {
733
- return callback(_core.errorUtils.getServiceUnavailable(), null, upLinksErrors);
1347
+ if (typeof data.time[version] === 'string') {
1348
+ // this should not h appen, but we keep this check to avoid or easy bug report
1349
+ this.logger.warn({
1350
+ name,
1351
+ version
1352
+ }, 'the time for the version @{version} already exists, it has been overwritten for package @{name}');
734
1353
  }
735
1354
 
736
- return callback(_core.errorUtils.getNotFound(_core.API_ERROR.NO_PACKAGE), null, upLinksErrors);
1355
+ data.time[version] = currentDate;
1356
+ debug('time added for %s version %s', name, version);
737
1357
  }
738
1358
 
739
- if (upLinks.length === 0) {
740
- return callback(null, packageInfo);
1359
+ data.versions[version] = metadata; // TODO: review this method, it's a bit ugly
1360
+
1361
+ (0, _2.tagVersion)(data, version, tag);
1362
+
1363
+ try {
1364
+ debug('%s` add on database', name);
1365
+ await this.localStorage.getStoragePlugin().add(name);
1366
+ this.logger.debug({
1367
+ name,
1368
+ version
1369
+ }, 'version @{version} added to database for @{name}');
1370
+ } catch (err) {
1371
+ throw _core.errorUtils.getBadData(err.message);
741
1372
  }
742
1373
 
743
- self.localStorage.updateVersions(name, packageInfo, async (err, packageJsonLocal) => {
744
- if (err) {
745
- return callback(err);
746
- } // Any error here will cause a 404, like an uplink error. This is likely
747
- // the right thing to do
748
- // as a broken filter is a security risk.
1374
+ return data;
1375
+ });
1376
+ }
1377
+ /**
1378
+ * Create an empty new local cache package without versions.
1379
+ * @param name name of the package
1380
+ * @returns
1381
+ */
1382
+
749
1383
 
1384
+ async createNewLocalCachePackage(name, latestVersion) {
1385
+ const storage = this.getPrivatePackageStorage(name);
750
1386
 
751
- const filterErrors = []; // This MUST be done serially and not in parallel as they modify packageJsonLocal
1387
+ if (!storage) {
1388
+ debug(`storage is missing for %o package cannot be added`, name);
1389
+ throw _core.errorUtils.getNotFound('this package cannot be added');
1390
+ }
1391
+
1392
+ const currentTime = new Date().toISOString();
1393
+ const packageData = { ...(0, _storageUtils.generatePackageTemplate)(name),
1394
+ time: {
1395
+ created: currentTime,
1396
+ modified: currentTime,
1397
+ [latestVersion]: currentTime
1398
+ }
1399
+ };
752
1400
 
753
- for (const filter of self.filters) {
754
- try {
755
- // These filters can assume it's save to modify packageJsonLocal
756
- // and return it directly for
757
- // performance (i.e. need not be pure)
758
- packageJsonLocal = await filter.filter_metadata(packageJsonLocal);
759
- } catch (err) {
760
- filterErrors.push(err);
1401
+ try {
1402
+ await storage.createPackage(name, packageData);
1403
+ this.logger.info({
1404
+ name
1405
+ }, 'created new package @{name}');
1406
+ return;
1407
+ } catch (err) {
1408
+ if (_lodash.default.isNull(err) === false && (err.code === _storageUtils.STORAGE.FILE_EXIST_ERROR || err.code === _core.HTTP_STATUS.CONFLICT)) {
1409
+ debug(`error on creating a package for %o with error %o`, name, err.message);
1410
+ throw _core.errorUtils.getConflict();
1411
+ }
1412
+
1413
+ return;
1414
+ }
1415
+ }
1416
+
1417
+ isAllowPublishOffline() {
1418
+ return typeof this.config.publish !== 'undefined' && _lodash.default.isBoolean(this.config.publish.allow_offline) && this.config.publish.allow_offline;
1419
+ }
1420
+ /**
1421
+ *
1422
+ * @param name package name
1423
+ * @param uplinksLook
1424
+ * @returns
1425
+ */
1426
+
1427
+
1428
+ async checkPackageRemote(name, uplinksLook) {
1429
+ try {
1430
+ // we provide a null manifest, thus the manifest returned will be the remote one
1431
+ const [remoteManifest, upLinksErrors] = await this.syncUplinksMetadataNext(name, null, {
1432
+ uplinksLook
1433
+ }); // checking package exist already
1434
+
1435
+ if ((0, _lodash.isNil)(remoteManifest) === false) {
1436
+ throw _core.errorUtils.getConflict(_core.API_ERROR.PACKAGE_EXIST);
1437
+ }
1438
+
1439
+ for (let errorItem = 0; errorItem < upLinksErrors.length; errorItem++) {
1440
+ // checking error
1441
+ // if uplink fails with a status other than 404, we report failure
1442
+ if ((0, _lodash.isNil)(upLinksErrors[errorItem][0]) === false) {
1443
+ if (upLinksErrors[errorItem][0].status !== _core.HTTP_STATUS.NOT_FOUND) {
1444
+ if (upLinksErrors) {
1445
+ return null;
1446
+ }
1447
+
1448
+ throw _core.errorUtils.getServiceUnavailable(_core.API_ERROR.UPLINK_OFFLINE_PUBLISH);
761
1449
  }
762
1450
  }
1451
+ }
763
1452
 
764
- callback(null, packageJsonLocal, _lodash.default.concat(upLinksErrors, filterErrors));
765
- });
1453
+ return remoteManifest;
1454
+ } catch (err) {
1455
+ if (err && err.status !== _core.HTTP_STATUS.NOT_FOUND) {
1456
+ throw err;
1457
+ }
1458
+
1459
+ return null;
1460
+ }
1461
+ }
1462
+
1463
+ setDefaultRevision(json) {
1464
+ // calculate revision from couch db
1465
+ if (_lodash.default.isString(json._rev) === false) {
1466
+ json._rev = _storageUtils.STORAGE.DEFAULT_REVISION;
1467
+ } // this is intended in debug mode we do not want modify the store revision
1468
+
1469
+
1470
+ if (_lodash.default.isNil(this.config._debug)) {
1471
+ json._rev = (0, _storageUtils.generateRevision)(json._rev);
1472
+ }
1473
+
1474
+ return json;
1475
+ }
1476
+
1477
+ async writePackageNext(name, json) {
1478
+ const storage = this.getPrivatePackageStorage(name);
1479
+
1480
+ if (_lodash.default.isNil(storage)) {
1481
+ // TODO: replace here 500 error
1482
+ throw _core.errorUtils.getBadData();
1483
+ }
1484
+
1485
+ await storage.savePackage(name, this.setDefaultRevision(json));
1486
+ }
1487
+ /**
1488
+ * @param {*} name package name
1489
+ * @param {*} updateHandler function(package, cb) - update function
1490
+ * @param {*} callback callback that gets invoked after it's all updated
1491
+ * @return {Function}
1492
+ */
1493
+
1494
+
1495
+ async updatePackageNext(name, updateHandler) {
1496
+ const storage = this.getPrivatePackageStorage(name);
1497
+
1498
+ if (!storage) {
1499
+ throw _core.errorUtils.getNotFound();
1500
+ } // we update the package on the local storage
1501
+
1502
+
1503
+ const updatedManifest = await storage.updatePackage(name, updateHandler); // after correctly updated write to the storage
1504
+
1505
+ try {
1506
+ await this.writePackageNext(name, (0, _storageUtils.normalizePackage)(updatedManifest));
1507
+ return updatedManifest;
1508
+ } catch (err) {
1509
+ if (err.code === resourceNotAvailable) {
1510
+ throw _core.errorUtils.getInternalError('resource temporarily unavailable');
1511
+ } else if (err.code === noSuchFile) {
1512
+ throw _core.errorUtils.getNotFound();
1513
+ } else {
1514
+ throw err;
1515
+ }
1516
+ }
1517
+ }
1518
+ /**
1519
+ *
1520
+ * @protected
1521
+ * @param {IGetPackageOptionsNext} options
1522
+ * @return {*} {Promise<[Manifest, any[]]>}
1523
+ * @memberof AbstractStorage
1524
+ */
1525
+
1526
+
1527
+ async getPackageNext(options) {
1528
+ const {
1529
+ name
1530
+ } = options;
1531
+ debug('get package for %o', name);
1532
+ let data = null;
1533
+
1534
+ try {
1535
+ data = await this.getPackageLocalMetadata(name);
1536
+ } catch (err) {
1537
+ // if error code is higher than 500 stop here
1538
+ if (err && (!err.status || err.status >= _core.HTTP_STATUS.INTERNAL_ERROR)) {
1539
+ throw err;
1540
+ }
1541
+ } // if we can't get the local metadata, we try to get the remote metadata
1542
+ // if we do to have local metadata, we try to update it with the upstream registry
1543
+
1544
+
1545
+ debug('sync uplinks for %o', name);
1546
+ const [remoteManifest, upLinksErrors] = await this.syncUplinksMetadataNext(name, data, {
1547
+ uplinksLook: options.uplinksLook,
1548
+ retry: options.retry,
1549
+ remoteAddress: options.requestOptions.remoteAddress // etag??
1550
+
1551
+ }); // if either local data and upstream data are empty, we throw an error
1552
+
1553
+ if (!remoteManifest && _lodash.default.isNull(data)) {
1554
+ throw _core.errorUtils.getNotFound(`${_core.API_ERROR.NOT_PACKAGE_UPLINK}: ${name}`); // if the remote manifest is empty, we return local data
1555
+ } else if (!remoteManifest && !_lodash.default.isNull(data)) {
1556
+ // no data on uplinks
1557
+ return [data, upLinksErrors];
1558
+ } // if we have local data, we try to update it with the upstream registry
1559
+
1560
+
1561
+ const normalizedPkg = Object.assign({}, remoteManifest, { // FIXME: clean up mutation within cleanUpLinksRef method
1562
+ ...(0, _storageUtils.normalizeDistTags)((0, _storageUtils.cleanUpLinksRef)(remoteManifest, options.keepUpLinkData)),
1563
+ _attachments: {}
766
1564
  });
1565
+ debug('no. sync uplinks errors %o for %s', upLinksErrors === null || upLinksErrors === void 0 ? void 0 : upLinksErrors.length, name);
1566
+ return [normalizedPkg, upLinksErrors];
767
1567
  }
768
1568
  /**
769
- * Set a hidden value for each version.
770
- * @param {Array} versions list of version
771
- * @param {String} upLink uplink name
1569
+ * Function fetches package metadata from uplinks and synchronizes it with local data
1570
+ if package is available locally, it MUST be provided in pkginfo.
1571
+ Using this example:
1572
+ "jquery":
1573
+ access: $all
1574
+ publish: $authenticated
1575
+ unpublish: $authenticated
1576
+ # two uplinks setup
1577
+ proxy: ver npmjs
1578
+ # one uplink setup
1579
+ proxy: npmjs
1580
+ A package requires uplinks syncronization if enables the proxy section, uplinks
1581
+ can be more than one, the more are the most slow request will take, the request
1582
+ are made in serie and if 1st call fails, the second will be triggered, otherwise
1583
+ the 1st will reply and others will be discareded. The order is important.
1584
+ Errors on upkinks are considered are, time outs, connection fails and http status 304,
1585
+ in that case the request returns empty body and we want ask next on the list if has fresh
1586
+ updates.
1587
+ */
1588
+
1589
+
1590
+ async syncUplinksMetadataNext(name, localManifest, options = {}) {
1591
+ let found = localManifest !== null;
1592
+ let syncManifest = null;
1593
+ const upLinks = [];
1594
+ const hasToLookIntoUplinks = _lodash.default.isNil(options.uplinksLook) || options.uplinksLook;
1595
+ debug('is sync uplink enabled %o', hasToLookIntoUplinks);
1596
+
1597
+ for (const uplink in this.uplinks) {
1598
+ if ((0, _config.hasProxyTo)(name, uplink, this.config.packages) && hasToLookIntoUplinks) {
1599
+ debug('sync uplink %o', uplink);
1600
+ upLinks.push(uplink);
1601
+ }
1602
+ } // if none uplink match we return the local manifest
1603
+
1604
+
1605
+ if (upLinks.length === 0) {
1606
+ debug('no uplinks found for %o upstream update aborted', name);
1607
+ return [localManifest, []];
1608
+ }
1609
+
1610
+ const uplinksErrors = []; // we resolve uplinks async in serie, first come first serve
1611
+
1612
+ for (const uplink of upLinks) {
1613
+ try {
1614
+ const tempManifest = _lodash.default.isNil(localManifest) ? (0, _storageUtils.generatePackageTemplate)(name) : { ...localManifest
1615
+ };
1616
+ syncManifest = await this.mergeCacheRemoteMetadata(this.uplinks[uplink], tempManifest, options);
1617
+ debug('syncing on uplink %o', syncManifest.name);
1618
+
1619
+ if (_lodash.default.isNil(syncManifest) === false) {
1620
+ found = true;
1621
+ break;
1622
+ }
1623
+ } catch (err) {
1624
+ debug('error captured on uplink %o', err.message);
1625
+ uplinksErrors.push(err); // enforce use next uplink on the list
1626
+
1627
+ continue;
1628
+ }
1629
+ }
1630
+
1631
+ if (found && syncManifest !== null) {
1632
+ // updates the local cache manifest with fresh data
1633
+ let updatedCacheManifest = await this.updateVersionsNext(name, syncManifest); // plugin filter applied to the manifest
1634
+
1635
+ const [filteredManifest, filtersErrors] = await this.applyFilters(updatedCacheManifest);
1636
+ return [{ ...updatedCacheManifest,
1637
+ ...filteredManifest
1638
+ }, [...uplinksErrors, ...filtersErrors]];
1639
+ } else if (found && _lodash.default.isNil(localManifest) === false) {
1640
+ return [localManifest, uplinksErrors];
1641
+ } else {
1642
+ // if is not found, calculate the right error to return
1643
+ debug('uplinks sync failed with %o errors', uplinksErrors.length);
1644
+
1645
+ for (const err of uplinksErrors) {
1646
+ const {
1647
+ code
1648
+ } = err;
1649
+
1650
+ if (code === 'ETIMEDOUT' || code === 'ESOCKETTIMEDOUT' || code === 'ECONNRESET') {
1651
+ debug('uplinks sync failed with timeout error');
1652
+ throw _core.errorUtils.getServiceUnavailable(err.code);
1653
+ } // we bubble up the 304 special error case
1654
+
1655
+
1656
+ if (code === _core.HTTP_STATUS.NOT_MODIFIED) {
1657
+ debug('uplinks sync failed with 304 error');
1658
+ throw err;
1659
+ }
1660
+ }
1661
+
1662
+ debug('uplinks sync failed with no package found');
1663
+ throw _core.errorUtils.getNotFound(_core.API_ERROR.NO_PACKAGE);
1664
+ }
1665
+ }
1666
+ /**
1667
+ * Merge a manifest with a remote manifest.
1668
+ *
1669
+ * If the uplinks are not available, the local manifest is returned.
1670
+ * If the uplinks are available, the local manifest is merged with the remote one.
1671
+ *
1672
+ *
1673
+ * @param uplink uplink instance
1674
+ * @param cachedManifest the local cached manifest
1675
+ * @param options options
1676
+ * @returns Returns a promise that resolves with the merged manifest.
1677
+ */
1678
+
1679
+
1680
+ async mergeCacheRemoteMetadata(uplink, cachedManifest, options) {
1681
+ // we store which uplink is updating the manifest
1682
+ const upLinkMeta = cachedManifest._uplinks[uplink.upname];
1683
+ let _cacheManifest = { ...cachedManifest
1684
+ };
1685
+
1686
+ if (_core.validatioUtils.isObject(upLinkMeta)) {
1687
+ const fetched = upLinkMeta.fetched; // we check the uplink cache is fresh
1688
+
1689
+ if (fetched && Date.now() - fetched < uplink.maxage) {
1690
+ return cachedManifest;
1691
+ }
1692
+ }
1693
+
1694
+ const remoteOptions = Object.assign({}, options, {
1695
+ etag: upLinkMeta === null || upLinkMeta === void 0 ? void 0 : upLinkMeta.etag
1696
+ }); // get the latest metadata from the uplink
1697
+
1698
+ const [remoteManifest, etag] = await uplink.getRemoteMetadataNext(_cacheManifest.name, remoteOptions);
1699
+
1700
+ try {
1701
+ _cacheManifest = _core.validatioUtils.normalizeMetadata(_cacheManifest, _cacheManifest.name);
1702
+ } catch (err) {
1703
+ this.logger.error({
1704
+ err: err
1705
+ }, 'package.json validating error @{!err?.message}\n@{err.stack}');
1706
+ throw err;
1707
+ } // updates the _uplink metadata fields, cache, etc
1708
+
1709
+
1710
+ _cacheManifest = (0, _storageUtils.updateUpLinkMetadata)(uplink.upname, _cacheManifest, etag); // merge time field cache and remote
1711
+
1712
+ _cacheManifest = (0, _storageUtils.mergeUplinkTimeIntoLocalNext)(_cacheManifest, remoteManifest); // update the _uplinks field in the cache
1713
+
1714
+ _cacheManifest = (0, _uplinkUtil.updateVersionsHiddenUpLinkNext)(_cacheManifest, uplink);
1715
+
1716
+ try {
1717
+ // merge versions from remote into the cache
1718
+ _cacheManifest = (0, _storageUtils.mergeVersions)(_cacheManifest, remoteManifest);
1719
+ return _cacheManifest;
1720
+ } catch (err) {
1721
+ this.logger.error({
1722
+ err: err
1723
+ }, 'package.json merge has failed @{!err?.message}\n@{err.stack}');
1724
+ throw err;
1725
+ }
1726
+ }
1727
+ /**
1728
+ * Apply filters to manifest.
1729
+ * @param manifest
1730
+ * @returns
1731
+ */
1732
+
1733
+
1734
+ async applyFilters(manifest) {
1735
+ if (this.filters.length === 0) {
1736
+ return [manifest, []];
1737
+ }
1738
+
1739
+ let filterPluginErrors = [];
1740
+ let filteredManifest = { ...manifest
1741
+ };
1742
+
1743
+ for (const filter of this.filters) {
1744
+ // These filters can assume it's save to modify packageJsonLocal
1745
+ // and return it directly for
1746
+ // performance (i.e. need not be pure)
1747
+ try {
1748
+ filteredManifest = await filter.filter_metadata(manifest);
1749
+ } catch (err) {
1750
+ this.logger.error({
1751
+ err: err.message
1752
+ }, 'filter has failed @{err}');
1753
+ filterPluginErrors.push(err);
1754
+ }
1755
+ }
1756
+
1757
+ return [filteredManifest, filterPluginErrors];
1758
+ }
1759
+
1760
+ _createNewPackageNext(name) {
1761
+ return (0, _storageUtils.normalizePackage)((0, _storageUtils.generatePackageTemplate)(name));
1762
+ }
1763
+ /**
1764
+ * Ensure the dist file remains as the same protocol
1765
+ * @param {Object} hash metadata
1766
+ * @param {String} upLinkKey registry key
772
1767
  * @private
1768
+ * @deprecated use _updateUplinkToRemoteProtocolNext (???)
773
1769
  */
774
1770
 
775
1771
 
776
- _updateVersionsHiddenUpLink(versions, upLink) {
777
- for (const i in versions) {
778
- if (Object.prototype.hasOwnProperty.call(versions, i)) {
779
- const version = versions[i]; // holds a "hidden" value to be used by the package storage.
780
- // $FlowFixMe
1772
+ updateUplinkToRemoteProtocol(hash, upLinkKey) {
1773
+ // if we got this information from a known registry,
1774
+ // use the same protocol for the tarball
1775
+ const tarballUrl = _url.default.parse(hash.url);
1776
+
1777
+ const uplinkUrl = _url.default.parse(this.config.uplinks[upLinkKey].url);
1778
+
1779
+ if (uplinkUrl.host === tarballUrl.host) {
1780
+ tarballUrl.protocol = uplinkUrl.protocol;
1781
+ hash.registry = upLinkKey;
1782
+ hash.url = _url.default.format(tarballUrl);
1783
+ }
1784
+ }
1785
+ /**
1786
+ * Create or read a package.
1787
+ *
1788
+ * If the package already exists, it will be read.
1789
+ * If the package is not found, it will be created.
1790
+ * If the error is anything else will throw an error
1791
+ *
1792
+ * @param {*} pkgName
1793
+ * @param {*} callback
1794
+ * @return {Function}
1795
+ */
1796
+
1797
+
1798
+ async readCreatePackage(pkgName) {
1799
+ const storage = this.getPrivatePackageStorage(pkgName);
1800
+
1801
+ if (_lodash.default.isNil(storage)) {
1802
+ throw _core.errorUtils.getInternalError('storage could not be found');
1803
+ }
1804
+
1805
+ try {
1806
+ const result = await storage.readPackage(pkgName);
1807
+ return (0, _storageUtils.normalizePackage)(result);
1808
+ } catch (err) {
1809
+ if (err.code === _storageUtils.STORAGE.NO_SUCH_FILE_ERROR || err.code === _core.HTTP_STATUS.NOT_FOUND) {
1810
+ return this._createNewPackageNext(pkgName);
1811
+ } else {
1812
+ this.logger.error({
1813
+ err: err,
1814
+ file: _storageUtils.STORAGE.PACKAGE_FILE_NAME
1815
+ }, `'error reading' @{file}: @{!err.message}`);
1816
+ throw _core.errorUtils.getInternalError();
1817
+ }
1818
+ }
1819
+ }
1820
+ /**
1821
+ Updates the local cache with the merge from the remote/client manifest.
1822
+ The steps are the following.
1823
+ 1. Get the latest version of the package from the cache.
1824
+ 2. If does not exist will return a
1825
+ @param name
1826
+ @param remoteManifest
1827
+ @returns return a merged manifest.
1828
+ */
1829
+
1830
+
1831
+ async updateVersionsNext(name, remoteManifest) {
1832
+ debug(`updating versions for package %o`, name);
1833
+ let cacheManifest = await this.readCreatePackage(name);
1834
+ let change = false; // updating readme
1835
+
1836
+ cacheManifest.readme = (0, _storageUtils.getLatestReadme)(remoteManifest);
1837
+
1838
+ if (remoteManifest.readme !== cacheManifest.readme) {
1839
+ debug('manifest readme updated for %o', name);
1840
+ change = true;
1841
+ }
1842
+
1843
+ debug('updating new remote versions');
1844
+
1845
+ for (const versionId in remoteManifest.versions) {
1846
+ // if detect a new remote version does not exist cache
1847
+ if (_lodash.default.isNil(cacheManifest.versions[versionId])) {
1848
+ var _version2, _version2$dist;
1849
+
1850
+ debug('new version from upstream %o', versionId);
1851
+ let version = remoteManifest.versions[versionId]; // we don't keep readme for package versions,
1852
+ // only one readme per package
1853
+ // TODO: readme clean up could be saved in configured eventually
1854
+
1855
+ version = (0, _2.cleanUpReadme)(version);
1856
+ debug('clean up readme for %o', versionId);
1857
+ version.contributors = (0, _utils.normalizeContributors)(version.contributors);
1858
+ change = true;
1859
+ cacheManifest.versions[versionId] = version;
1860
+
1861
+ if ((_version2 = version) !== null && _version2 !== void 0 && (_version2$dist = _version2.dist) !== null && _version2$dist !== void 0 && _version2$dist.tarball) {
1862
+ const filename = _core.pkgUtils.extractTarballName(version.dist.tarball); // store a fast access to the dist file by tarball name
1863
+ // it does NOT overwrite any existing records
1864
+
781
1865
 
782
- version[Symbol.for('__verdaccio_uplink')] = upLink.upname;
1866
+ if (_lodash.default.isNil(cacheManifest === null || cacheManifest === void 0 ? void 0 : cacheManifest._distfiles[filename])) {
1867
+ const hash = cacheManifest._distfiles[filename] = {
1868
+ url: version.dist.tarball,
1869
+ sha: version.dist.shasum
1870
+ }; // store cache metadata this the manifest
1871
+
1872
+ const upLink = version[Symbol.for('__verdaccio_uplink')];
1873
+
1874
+ if (_lodash.default.isNil(upLink) === false) {
1875
+ this.updateUplinkToRemoteProtocol(hash, upLink);
1876
+ }
1877
+ }
1878
+ }
1879
+ } else {
1880
+ debug('no new versions from upstream %s', name);
783
1881
  }
784
1882
  }
1883
+
1884
+ debug('update dist-tags');
1885
+
1886
+ for (const tag in remoteManifest[_core.DIST_TAGS]) {
1887
+ if (!cacheManifest[_core.DIST_TAGS][tag] || cacheManifest[_core.DIST_TAGS][tag] !== remoteManifest[_core.DIST_TAGS][tag]) {
1888
+ change = true;
1889
+ cacheManifest[_core.DIST_TAGS][tag] = remoteManifest[_core.DIST_TAGS][tag];
1890
+ }
1891
+ }
1892
+
1893
+ for (const up in remoteManifest._uplinks) {
1894
+ if (Object.prototype.hasOwnProperty.call(remoteManifest._uplinks, up)) {
1895
+ const need_change = !(0, _utils.isObject)(cacheManifest._uplinks[up]) || remoteManifest._uplinks[up].etag !== cacheManifest._uplinks[up].etag || remoteManifest._uplinks[up].fetched !== cacheManifest._uplinks[up].fetched;
1896
+
1897
+ if (need_change) {
1898
+ change = true;
1899
+ cacheManifest._uplinks[up] = remoteManifest._uplinks[up];
1900
+ }
1901
+ }
1902
+ }
1903
+
1904
+ debug('update time');
1905
+
1906
+ if ('time' in remoteManifest && !_lodash.default.isEqual(cacheManifest.time, remoteManifest.time)) {
1907
+ cacheManifest.time = remoteManifest.time;
1908
+ change = true;
1909
+ }
1910
+
1911
+ if (change) {
1912
+ debug('updating package info %o', name);
1913
+ await this.writePackageNext(name, cacheManifest);
1914
+ return cacheManifest;
1915
+ } else {
1916
+ return cacheManifest;
1917
+ }
785
1918
  }
786
1919
 
787
1920
  }