@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.
- package/CHANGELOG.md +92 -0
- package/build/index.d.ts +5 -4
- package/build/index.js +31 -15
- package/build/index.js.map +1 -1
- package/build/lib/TransFormResults.d.ts +15 -0
- package/build/lib/TransFormResults.js +61 -0
- package/build/lib/TransFormResults.js.map +1 -0
- package/build/lib/search-utils.d.ts +4 -0
- package/build/lib/search-utils.js +59 -0
- package/build/lib/search-utils.js.map +1 -0
- package/build/lib/star-utils.d.ts +14 -0
- package/build/{star-utils.js → lib/star-utils.js} +16 -2
- package/build/lib/star-utils.js.map +1 -0
- package/build/lib/storage-utils.d.ts +82 -0
- package/build/{storage-utils.js → lib/storage-utils.js} +173 -60
- package/build/lib/storage-utils.js.map +1 -0
- package/build/lib/uplink-util.d.ts +10 -0
- package/build/lib/uplink-util.js +49 -0
- package/build/lib/uplink-util.js.map +1 -0
- package/build/lib/versions-utils.d.ts +28 -0
- package/build/lib/versions-utils.js +104 -0
- package/build/lib/versions-utils.js.map +1 -0
- package/build/local-storage.d.ts +3 -165
- package/build/local-storage.js +6 -1166
- package/build/local-storage.js.map +1 -1
- package/build/storage.d.ts +242 -86
- package/build/storage.js +1681 -548
- package/build/storage.js.map +1 -1
- package/build/type.d.ts +42 -19
- package/build/type.js.map +1 -1
- package/jest.config.js +8 -2
- package/package.json +71 -71
- package/src/index.ts +5 -4
- package/src/lib/TransFormResults.ts +42 -0
- package/src/lib/search-utils.ts +50 -0
- package/src/{star-utils.ts → lib/star-utils.ts} +16 -5
- package/src/lib/storage-utils.ts +362 -0
- package/src/lib/uplink-util.ts +41 -0
- package/src/lib/versions-utils.ts +87 -0
- package/src/local-storage.ts +7 -1217
- package/src/storage.ts +1683 -635
- package/src/type.ts +47 -21
- package/test/fixtures/config/getTarballNext-getupstream.yaml +17 -0
- package/test/fixtures/config/syncDoubleUplinksMetadata.yaml +25 -0
- package/test/fixtures/config/syncNoUplinksMetadata.yaml +17 -0
- package/test/fixtures/config/syncSingleUplinksMetadata.yaml +21 -0
- package/test/fixtures/config/updateManifest-1.yaml +16 -0
- package/test/fixtures/manifests/foo-npmjs.json +253 -0
- package/test/fixtures/manifests/foo-verdaccio.json +253 -0
- package/test/fixtures/tarball.tgz +0 -0
- package/test/helpers.ts +22 -0
- package/test/local-store.__disabled__.ts +553 -0
- package/test/search.spec.ts +4 -5
- package/test/star.spec.ts +31 -0
- package/test/storage-utils.spec.ts +129 -42
- package/test/storage.spec.ts +943 -33
- package/test/versions.spec.ts +109 -0
- package/tsconfig.json +6 -3
- package/build/search.d.ts +0 -35
- package/build/search.js +0 -198
- package/build/search.js.map +0 -1
- package/build/star-utils.d.ts +0 -9
- package/build/star-utils.js.map +0 -1
- package/build/storage-utils.d.ts +0 -39
- package/build/storage-utils.js.map +0 -1
- package/build/uplink-util.d.ts +0 -7
- package/build/uplink-util.js +0 -38
- package/build/uplink-util.js.map +0 -1
- package/src/search.ts +0 -175
- package/src/storage-utils.ts +0 -263
- package/src/uplink-util.ts +0 -32
- 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 =
|
|
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
|
|
32
|
+
var _2 = require(".");
|
|
33
|
+
|
|
34
|
+
var _TransFormResults = require("./lib/TransFormResults");
|
|
35
|
+
|
|
36
|
+
var _searchUtils = require("./lib/search-utils");
|
|
31
37
|
|
|
32
|
-
var
|
|
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.
|
|
72
|
+
debug('uplinks available %o', Object.keys(this.uplinks));
|
|
56
73
|
}
|
|
57
74
|
|
|
58
|
-
|
|
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
|
-
*
|
|
76
|
-
Function
|
|
77
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
103
|
+
delete localData.versions[version];
|
|
104
|
+
delete localData.time[version];
|
|
125
105
|
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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
|
-
|
|
135
|
-
|
|
140
|
+
async removePackage(name, revision) {
|
|
141
|
+
debug('remove package %o', name);
|
|
142
|
+
await this.removePackageByRevision(name, revision);
|
|
136
143
|
}
|
|
137
144
|
/**
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
144
|
-
debug('
|
|
145
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
192
|
-
debug('remove packagefor package %o', name);
|
|
193
|
-
await this.localStorage.removePackage(name);
|
|
204
|
+
return manifest;
|
|
194
205
|
}
|
|
195
206
|
/**
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
228
|
-
debug('get tarball for package %o filename %o', name, filename);
|
|
229
|
-
const readStream = new _streams.ReadTarball({});
|
|
291
|
+
let cachedManifest = null;
|
|
230
292
|
|
|
231
|
-
|
|
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
|
-
|
|
239
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
-
|
|
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
|
-
|
|
304
|
-
|
|
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
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
|
|
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
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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
|
-
|
|
329
|
-
|
|
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
|
-
|
|
335
|
-
|
|
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
|
-
|
|
354
|
-
|
|
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
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
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
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
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
|
-
|
|
589
|
+
version.time = time; // Add for stars api
|
|
590
|
+
// @ts-ignore
|
|
486
591
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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
|
-
|
|
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
|
-
|
|
539
|
-
|
|
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
|
-
|
|
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
|
-
|
|
553
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
571
|
-
* @param
|
|
628
|
+
* Initialize the storage asynchronously.
|
|
629
|
+
* @param config Config
|
|
630
|
+
* @param filters IPluginFilters
|
|
631
|
+
* @returns Storage instance
|
|
572
632
|
*/
|
|
573
633
|
|
|
574
634
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
this.localStorage.
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
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
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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('
|
|
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
|
-
*
|
|
628
|
-
|
|
629
|
-
|
|
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
|
-
|
|
634
|
-
|
|
635
|
-
|
|
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 (
|
|
641
|
-
|
|
642
|
-
packageInfo = (0, _storageUtils.generatePackageTemplate)(name);
|
|
835
|
+
if (typeof storage === 'undefined') {
|
|
836
|
+
throw _core.errorUtils.getServiceUnavailable('storage not initialized');
|
|
643
837
|
}
|
|
644
838
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
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
|
-
|
|
654
|
-
|
|
861
|
+
createFailureStreamResponseNext() {
|
|
862
|
+
const stream = new _stream.PassThrough(); // we ensure fails on the next tick into the event loop
|
|
655
863
|
|
|
656
|
-
|
|
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
|
-
|
|
662
|
-
|
|
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
|
-
|
|
892
|
+
const version = tags[tag];
|
|
893
|
+
newData = (0, _2.tagVersionNext)(newData, version, tag);
|
|
666
894
|
}
|
|
667
895
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
}
|
|
896
|
+
return newData;
|
|
897
|
+
});
|
|
898
|
+
}
|
|
672
899
|
|
|
673
|
-
|
|
674
|
-
|
|
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
|
-
|
|
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
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
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
|
-
|
|
688
|
-
|
|
689
|
-
|
|
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
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
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
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
(
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
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 (
|
|
733
|
-
|
|
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
|
-
|
|
1355
|
+
data.time[version] = currentDate;
|
|
1356
|
+
debug('time added for %s version %s', name, version);
|
|
737
1357
|
}
|
|
738
1358
|
|
|
739
|
-
|
|
740
|
-
|
|
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
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
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
|
-
|
|
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
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
770
|
-
|
|
771
|
-
|
|
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
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
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
|
-
|
|
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
|
}
|