@verdaccio/store 6.0.0-6-next.18 → 6.0.0-6-next.21
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 +41 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +13 -0
- package/build/index.js.map +1 -1
- package/build/local-storage.d.ts +22 -1
- package/build/local-storage.js +240 -4
- package/build/local-storage.js.map +1 -1
- package/build/search.d.ts +0 -43
- package/build/search.js +31 -142
- package/build/search.js.map +1 -1
- package/build/star-utils.d.ts +9 -0
- package/build/star-utils.js +50 -0
- package/build/star-utils.js.map +1 -0
- package/build/storage-utils.d.ts +3 -1
- package/build/storage-utils.js +20 -5
- package/build/storage-utils.js.map +1 -1
- package/build/storage.d.ts +23 -1
- package/build/storage.js +149 -53
- package/build/storage.js.map +1 -1
- package/build/type.d.ts +10 -1
- package/package.json +14 -18
- package/src/index.ts +1 -0
- package/src/local-storage.ts +255 -4
- package/src/search.ts +21 -124
- package/src/star-utils.ts +38 -0
- package/src/storage-utils.ts +21 -6
- package/src/storage.ts +188 -62
- package/src/type.ts +11 -1
- package/test/search.spec.ts +2 -66
- package/test/storage-utils.spec.ts +95 -1
- package/test/storage.spec.ts +7 -7
package/src/local-storage.ts
CHANGED
|
@@ -43,6 +43,8 @@ import {
|
|
|
43
43
|
|
|
44
44
|
const debug = buildDebug('verdaccio:storage:local');
|
|
45
45
|
|
|
46
|
+
export const noSuchFile = 'ENOENT';
|
|
47
|
+
export const resourceNotAvailable = 'EAGAIN';
|
|
46
48
|
export type IPluginStorage = pluginUtils.IPluginStorage<Config>;
|
|
47
49
|
|
|
48
50
|
export function normalizeSearchPackage(
|
|
@@ -365,6 +367,144 @@ class LocalStorage {
|
|
|
365
367
|
);
|
|
366
368
|
}
|
|
367
369
|
|
|
370
|
+
public async addVersionNext(
|
|
371
|
+
name: string,
|
|
372
|
+
version: string,
|
|
373
|
+
metadata: Version,
|
|
374
|
+
tag: StringValue
|
|
375
|
+
): Promise<void> {
|
|
376
|
+
debug(`add version %s package for %s`, version, name);
|
|
377
|
+
await this.updatePackageNext(name, async (data: Package): Promise<Package> => {
|
|
378
|
+
debug('%s package is being updated', name);
|
|
379
|
+
// keep only one readme per package
|
|
380
|
+
data.readme = metadata.readme;
|
|
381
|
+
debug('%s` readme mutated', name);
|
|
382
|
+
// TODO: lodash remove
|
|
383
|
+
metadata = cleanUpReadme(metadata);
|
|
384
|
+
metadata.contributors = normalizeContributors(metadata.contributors as Author[]);
|
|
385
|
+
debug('%s` contributors normalized', name);
|
|
386
|
+
const hasVersion = data.versions[version] != null;
|
|
387
|
+
if (hasVersion) {
|
|
388
|
+
debug('%s version %s already exists', name, version);
|
|
389
|
+
throw errorUtils.getConflict();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// if uploaded tarball has a different shasum, it's very likely that we
|
|
393
|
+
// have some kind of error
|
|
394
|
+
if (validatioUtils.isObject(metadata.dist) && _.isString(metadata.dist.tarball)) {
|
|
395
|
+
const tarball = metadata.dist.tarball.replace(/.*\//, '');
|
|
396
|
+
|
|
397
|
+
if (validatioUtils.isObject(data._attachments[tarball])) {
|
|
398
|
+
if (
|
|
399
|
+
_.isNil(data._attachments[tarball].shasum) === false &&
|
|
400
|
+
_.isNil(metadata.dist.shasum) === false
|
|
401
|
+
) {
|
|
402
|
+
if (data._attachments[tarball].shasum != metadata.dist.shasum) {
|
|
403
|
+
const errorMessage =
|
|
404
|
+
`shasum error, ` +
|
|
405
|
+
`${data._attachments[tarball].shasum} != ${metadata.dist.shasum}`;
|
|
406
|
+
throw errorUtils.getBadRequest(errorMessage);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const currentDate = new Date().toISOString();
|
|
411
|
+
|
|
412
|
+
// some old storage do not have this field #740
|
|
413
|
+
if (_.isNil(data.time)) {
|
|
414
|
+
data.time = {};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
data.time['modified'] = currentDate;
|
|
418
|
+
|
|
419
|
+
if ('created' in data.time === false) {
|
|
420
|
+
data.time.created = currentDate;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
data.time[version] = currentDate;
|
|
424
|
+
data._attachments[tarball].version = version;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
data.versions[version] = metadata;
|
|
429
|
+
tagVersion(data, version, tag);
|
|
430
|
+
|
|
431
|
+
try {
|
|
432
|
+
debug('%s` add on database', name);
|
|
433
|
+
await this.storagePlugin.add(name);
|
|
434
|
+
} catch (err: any) {
|
|
435
|
+
throw errorUtils.getBadData(err.message);
|
|
436
|
+
}
|
|
437
|
+
return data;
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
// this._updatePackage(
|
|
441
|
+
// name,
|
|
442
|
+
// async (data, cb: Callback): Promise<void> => {
|
|
443
|
+
// debug('%s package is being updated', name);
|
|
444
|
+
// // keep only one readme per package
|
|
445
|
+
// data.readme = metadata.readme;
|
|
446
|
+
// debug('%s` readme mutated', name);
|
|
447
|
+
// // TODO: lodash remove
|
|
448
|
+
// metadata = cleanUpReadme(metadata);
|
|
449
|
+
// metadata.contributors = normalizeContributors(metadata.contributors as Author[]);
|
|
450
|
+
// debug('%s` contributors normalized', name);
|
|
451
|
+
// const hasVersion = data.versions[version] != null;
|
|
452
|
+
// if (hasVersion) {
|
|
453
|
+
// debug('%s version %s already exists', name, version);
|
|
454
|
+
// return cb(errorUtils.getConflict());
|
|
455
|
+
// }
|
|
456
|
+
|
|
457
|
+
// // if uploaded tarball has a different shasum, it's very likely that we
|
|
458
|
+
// // have some kind of error
|
|
459
|
+
// if (validatioUtils.isObject(metadata.dist) && _.isString(metadata.dist.tarball)) {
|
|
460
|
+
// const tarball = metadata.dist.tarball.replace(/.*\//, '');
|
|
461
|
+
|
|
462
|
+
// if (validatioUtils.isObject(data._attachments[tarball])) {
|
|
463
|
+
// if (
|
|
464
|
+
// _.isNil(data._attachments[tarball].shasum) === false &&
|
|
465
|
+
// _.isNil(metadata.dist.shasum) === false
|
|
466
|
+
// ) {
|
|
467
|
+
// if (data._attachments[tarball].shasum != metadata.dist.shasum) {
|
|
468
|
+
// const errorMessage =
|
|
469
|
+
// `shasum error, ` +
|
|
470
|
+
// `${data._attachments[tarball].shasum} != ${metadata.dist.shasum}`;
|
|
471
|
+
// return cb(errorUtils.getBadRequest(errorMessage));
|
|
472
|
+
// }
|
|
473
|
+
// }
|
|
474
|
+
|
|
475
|
+
// const currentDate = new Date().toISOString();
|
|
476
|
+
|
|
477
|
+
// // some old storage do not have this field #740
|
|
478
|
+
// if (_.isNil(data.time)) {
|
|
479
|
+
// data.time = {};
|
|
480
|
+
// }
|
|
481
|
+
|
|
482
|
+
// data.time['modified'] = currentDate;
|
|
483
|
+
|
|
484
|
+
// if ('created' in data.time === false) {
|
|
485
|
+
// data.time.created = currentDate;
|
|
486
|
+
// }
|
|
487
|
+
|
|
488
|
+
// data.time[version] = currentDate;
|
|
489
|
+
// data._attachments[tarball].version = version;
|
|
490
|
+
// }
|
|
491
|
+
// }
|
|
492
|
+
|
|
493
|
+
// data.versions[version] = metadata;
|
|
494
|
+
// tagVersion(data, version, tag);
|
|
495
|
+
|
|
496
|
+
// try {
|
|
497
|
+
// debug('%s` add on database', name);
|
|
498
|
+
// await this.storagePlugin.add(name);
|
|
499
|
+
// cb();
|
|
500
|
+
// } catch (err: any) {
|
|
501
|
+
// cb(errorUtils.getBadData(err.message));
|
|
502
|
+
// }
|
|
503
|
+
// },
|
|
504
|
+
// callback
|
|
505
|
+
// );
|
|
506
|
+
}
|
|
507
|
+
|
|
368
508
|
/**
|
|
369
509
|
* Merge a new list of tags for a local packages with the existing one.
|
|
370
510
|
* @param {*} pkgName
|
|
@@ -408,7 +548,7 @@ class LocalStorage {
|
|
|
408
548
|
public changePackage(
|
|
409
549
|
name: string,
|
|
410
550
|
incomingPkg: Package,
|
|
411
|
-
revision: string |
|
|
551
|
+
revision: string | undefined,
|
|
412
552
|
callback: Callback
|
|
413
553
|
): void {
|
|
414
554
|
debug(`change package tags for %o revision %s`, name, revision);
|
|
@@ -471,6 +611,70 @@ class LocalStorage {
|
|
|
471
611
|
}
|
|
472
612
|
);
|
|
473
613
|
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Update the package metadata, tags and attachments (tarballs).
|
|
617
|
+
* Note: Currently supports unpublishing and deprecation.
|
|
618
|
+
* @param {*} name
|
|
619
|
+
* @param {*} incomingPkg
|
|
620
|
+
* @param {*} revision
|
|
621
|
+
* @param {*} callback
|
|
622
|
+
* @return {Function}
|
|
623
|
+
*/
|
|
624
|
+
public async changePackageNext(
|
|
625
|
+
name: string,
|
|
626
|
+
incomingPkg: Package,
|
|
627
|
+
revision: string | undefined
|
|
628
|
+
): Promise<void> {
|
|
629
|
+
debug(`change package tags for %o revision %s`, name, revision);
|
|
630
|
+
if (
|
|
631
|
+
!validatioUtils.isObject(incomingPkg.versions) ||
|
|
632
|
+
!validatioUtils.isObject(incomingPkg[DIST_TAGS])
|
|
633
|
+
) {
|
|
634
|
+
debug(`change package bad data for %o`, name);
|
|
635
|
+
throw errorUtils.getBadData();
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
debug(`change package udapting package for %o`, name);
|
|
639
|
+
await this.updatePackageNext(name, async (localData: Package): Promise<Package> => {
|
|
640
|
+
for (const version in localData.versions) {
|
|
641
|
+
const incomingVersion = incomingPkg.versions[version];
|
|
642
|
+
if (_.isNil(incomingVersion)) {
|
|
643
|
+
this.logger.info({ name: name, version: version }, 'unpublishing @{name}@@{version}');
|
|
644
|
+
|
|
645
|
+
// FIXME: I prefer return a new object rather mutate the metadata
|
|
646
|
+
delete localData.versions[version];
|
|
647
|
+
delete localData.time![version];
|
|
648
|
+
|
|
649
|
+
for (const file in localData._attachments) {
|
|
650
|
+
if (localData._attachments[file].version === version) {
|
|
651
|
+
delete localData._attachments[file].version;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
} else if (Object.prototype.hasOwnProperty.call(incomingVersion, 'deprecated')) {
|
|
655
|
+
const incomingDeprecated = incomingVersion.deprecated;
|
|
656
|
+
if (incomingDeprecated != localData.versions[version].deprecated) {
|
|
657
|
+
if (!incomingDeprecated) {
|
|
658
|
+
this.logger.info(
|
|
659
|
+
{ name: name, version: version },
|
|
660
|
+
'undeprecating @{name}@@{version}'
|
|
661
|
+
);
|
|
662
|
+
delete localData.versions[version].deprecated;
|
|
663
|
+
} else {
|
|
664
|
+
this.logger.info({ name: name, version: version }, 'deprecating @{name}@@{version}');
|
|
665
|
+
localData.versions[version].deprecated = incomingDeprecated;
|
|
666
|
+
}
|
|
667
|
+
localData.time!.modified = new Date().toISOString();
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
localData[USERS] = incomingPkg[USERS];
|
|
673
|
+
localData[DIST_TAGS] = incomingPkg[DIST_TAGS];
|
|
674
|
+
return localData;
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
|
|
474
678
|
/**
|
|
475
679
|
* Remove a tarball.
|
|
476
680
|
* @param {*} name
|
|
@@ -754,10 +958,16 @@ class LocalStorage {
|
|
|
754
958
|
reject(err);
|
|
755
959
|
}
|
|
756
960
|
|
|
961
|
+
if (_.isEmpty(pkg?.versions)) {
|
|
962
|
+
return resolve({});
|
|
963
|
+
}
|
|
964
|
+
|
|
757
965
|
const searchPackage = normalizeSearchPackage(pkg, searchItem);
|
|
758
966
|
const searchPackageItem: searchUtils.SearchPackageItem = {
|
|
759
967
|
package: searchPackage,
|
|
760
968
|
score: searchItem.score,
|
|
969
|
+
verdaccioPkgCached: searchItem.verdaccioPkgCached,
|
|
970
|
+
verdaccioPrivate: searchItem.verdaccioPrivate,
|
|
761
971
|
flags: searchItem?.flags,
|
|
762
972
|
// FUTURE: find a better way to calculate the score
|
|
763
973
|
searchScore: 1,
|
|
@@ -890,12 +1100,12 @@ class LocalStorage {
|
|
|
890
1100
|
private _updatePackage(
|
|
891
1101
|
name: string,
|
|
892
1102
|
updateHandler: StorageUpdateCallback,
|
|
893
|
-
|
|
1103
|
+
onEnd: Callback
|
|
894
1104
|
): void {
|
|
895
1105
|
const storage: IPackageStorage = this._getLocalStorage(name);
|
|
896
1106
|
|
|
897
1107
|
if (!storage) {
|
|
898
|
-
return
|
|
1108
|
+
return onEnd(errorUtils.getNotFound());
|
|
899
1109
|
}
|
|
900
1110
|
|
|
901
1111
|
storage.updatePackage(
|
|
@@ -903,10 +1113,42 @@ class LocalStorage {
|
|
|
903
1113
|
updateHandler,
|
|
904
1114
|
this._writePackage.bind(this),
|
|
905
1115
|
normalizePackage,
|
|
906
|
-
|
|
1116
|
+
onEnd
|
|
907
1117
|
);
|
|
908
1118
|
}
|
|
909
1119
|
|
|
1120
|
+
/**
|
|
1121
|
+
* @param {*} name package name
|
|
1122
|
+
* @param {*} updateHandler function(package, cb) - update function
|
|
1123
|
+
* @param {*} callback callback that gets invoked after it's all updated
|
|
1124
|
+
* @return {Function}
|
|
1125
|
+
*/
|
|
1126
|
+
private async updatePackageNext(
|
|
1127
|
+
name: string,
|
|
1128
|
+
updateHandler: (manifest: Package) => Promise<Package>
|
|
1129
|
+
): Promise<void> {
|
|
1130
|
+
const storage: IPackageStorage = this._getLocalStorage(name);
|
|
1131
|
+
|
|
1132
|
+
if (!storage) {
|
|
1133
|
+
throw errorUtils.getNotFound();
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// we update the package on the local storage
|
|
1137
|
+
const updatedManifest: Package = await storage.updatePackageNext(name, updateHandler);
|
|
1138
|
+
// after correctly updated write to the storage
|
|
1139
|
+
try {
|
|
1140
|
+
await this.writePackageNext(name, normalizePackage(updatedManifest));
|
|
1141
|
+
} catch (err: any) {
|
|
1142
|
+
if (err.code === resourceNotAvailable) {
|
|
1143
|
+
throw errorUtils.getInternalError('resource temporarily unavailable');
|
|
1144
|
+
} else if (err.code === noSuchFile) {
|
|
1145
|
+
throw errorUtils.getNotFound();
|
|
1146
|
+
} else {
|
|
1147
|
+
throw err;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
|
|
910
1152
|
/**
|
|
911
1153
|
* Update the revision (_rev) string for a package.
|
|
912
1154
|
* @param {*} name
|
|
@@ -922,6 +1164,15 @@ class LocalStorage {
|
|
|
922
1164
|
storage.savePackage(name, this._setDefaultRevision(json), callback);
|
|
923
1165
|
}
|
|
924
1166
|
|
|
1167
|
+
private async writePackageNext(name: string, json: Package): Promise<void> {
|
|
1168
|
+
const storage: any = this._getLocalStorage(name);
|
|
1169
|
+
if (_.isNil(storage)) {
|
|
1170
|
+
// TODO: replace here 500 error
|
|
1171
|
+
throw errorUtils.getBadData();
|
|
1172
|
+
}
|
|
1173
|
+
await storage.savePackageNext(name, this._setDefaultRevision(json));
|
|
1174
|
+
}
|
|
1175
|
+
|
|
925
1176
|
private _setDefaultRevision(json: Package): Package {
|
|
926
1177
|
// calculate revision from couch db
|
|
927
1178
|
if (_.isString(json._rev) === false) {
|
package/src/search.ts
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
// eslint-disable no-invalid-this
|
|
3
3
|
import buildDebug from 'debug';
|
|
4
4
|
import _ from 'lodash';
|
|
5
|
-
import lunr from 'lunr';
|
|
6
|
-
import lunrMutable from 'lunr-mutable-indexes';
|
|
7
5
|
import { PassThrough, Transform, pipeline } from 'stream';
|
|
8
6
|
|
|
9
7
|
import { VerdaccioError } from '@verdaccio/core';
|
|
@@ -20,8 +18,8 @@ export interface ISearchResult {
|
|
|
20
18
|
ref: string;
|
|
21
19
|
score: number;
|
|
22
20
|
}
|
|
21
|
+
// @deprecated not longer used
|
|
23
22
|
export interface IWebSearch {
|
|
24
|
-
index: lunrMutable.index;
|
|
25
23
|
storage: Storage;
|
|
26
24
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
25
|
query(query: string): ISearchResult[];
|
|
@@ -33,7 +31,8 @@ export interface IWebSearch {
|
|
|
33
31
|
|
|
34
32
|
export function removeDuplicates(results: searchUtils.SearchPackageItem[]) {
|
|
35
33
|
const pkgNames: any[] = [];
|
|
36
|
-
|
|
34
|
+
const orderByResults = _.orderBy(results, ['verdaccioPrivate', 'asc']);
|
|
35
|
+
return orderByResults.filter((pkg) => {
|
|
37
36
|
if (pkgNames.includes(pkg?.package?.name)) {
|
|
38
37
|
return false;
|
|
39
38
|
}
|
|
@@ -58,16 +57,18 @@ class TransFormResults extends Transform {
|
|
|
58
57
|
*/
|
|
59
58
|
public _transform(chunk, _encoding, callback) {
|
|
60
59
|
if (_.isArray(chunk)) {
|
|
60
|
+
// from remotes we should expect chunks as arrays
|
|
61
61
|
(chunk as searchUtils.SearchItem[])
|
|
62
62
|
.filter((pkgItem) => {
|
|
63
63
|
debug(`streaming remote pkg name ${pkgItem?.package?.name}`);
|
|
64
64
|
return true;
|
|
65
65
|
})
|
|
66
66
|
.forEach((pkgItem) => {
|
|
67
|
-
this.push(pkgItem);
|
|
67
|
+
this.push({ ...pkgItem, verdaccioPkgCached: false, verdaccioPrivate: false });
|
|
68
68
|
});
|
|
69
69
|
return callback();
|
|
70
70
|
} else {
|
|
71
|
+
// local we expect objects
|
|
71
72
|
debug(`streaming local pkg name ${chunk?.package?.name}`);
|
|
72
73
|
this.push(chunk);
|
|
73
74
|
return callback();
|
|
@@ -105,30 +106,34 @@ export class SearchManager {
|
|
|
105
106
|
if (!uplink) {
|
|
106
107
|
// this should never tecnically happens
|
|
107
108
|
logger.fatal({ uplinkId }, 'uplink @upLinkId not found');
|
|
108
|
-
throw new Error(`uplink ${uplinkId} not found`);
|
|
109
109
|
}
|
|
110
110
|
return this.consumeSearchStream(uplinkId, uplink, options, streamPassThrough);
|
|
111
111
|
});
|
|
112
112
|
|
|
113
113
|
try {
|
|
114
114
|
debug('search uplinks');
|
|
115
|
-
|
|
115
|
+
// we only process those streams end successfully, if all fails
|
|
116
|
+
// we just include local storage
|
|
117
|
+
await Promise.allSettled([...searchUplinksStreams]);
|
|
116
118
|
debug('search uplinks done');
|
|
117
|
-
} catch (err) {
|
|
118
|
-
logger.error({ err }, ' error on uplinks search @{err}');
|
|
119
|
+
} catch (err: any) {
|
|
120
|
+
logger.error({ err: err?.message }, ' error on uplinks search @{err}');
|
|
119
121
|
streamPassThrough.emit('error', err);
|
|
120
|
-
throw err;
|
|
121
122
|
}
|
|
122
123
|
debug('search local');
|
|
123
|
-
|
|
124
|
-
|
|
124
|
+
try {
|
|
125
|
+
await this.localStorage.search(streamPassThrough, options.query as searchUtils.SearchQuery);
|
|
126
|
+
} catch (err: any) {
|
|
127
|
+
logger.error({ err: err?.message }, ' error on local search @{err}');
|
|
128
|
+
streamPassThrough.emit('error', err);
|
|
129
|
+
}
|
|
125
130
|
const data: searchUtils.SearchPackageItem[] = [];
|
|
126
131
|
const outPutStream = new PassThrough({ objectMode: true });
|
|
127
132
|
pipeline(streamPassThrough, transformResults, outPutStream, (err) => {
|
|
128
133
|
if (err) {
|
|
129
134
|
throw errorUtils.getInternalError(err ? err.message : 'unknown error');
|
|
130
135
|
} else {
|
|
131
|
-
debug('
|
|
136
|
+
debug('pipeline succeeded');
|
|
132
137
|
}
|
|
133
138
|
});
|
|
134
139
|
|
|
@@ -138,9 +143,9 @@ export class SearchManager {
|
|
|
138
143
|
|
|
139
144
|
return new Promise((resolve) => {
|
|
140
145
|
outPutStream.on('finish', async () => {
|
|
141
|
-
const
|
|
142
|
-
debug('stream
|
|
143
|
-
return resolve(
|
|
146
|
+
const searchFinalResults: searchUtils.SearchPackageItem[] = removeDuplicates(data);
|
|
147
|
+
debug('search stream total results: %o', searchFinalResults.length);
|
|
148
|
+
return resolve(searchFinalResults);
|
|
144
149
|
});
|
|
145
150
|
debug('search done');
|
|
146
151
|
});
|
|
@@ -168,111 +173,3 @@ export class SearchManager {
|
|
|
168
173
|
});
|
|
169
174
|
}
|
|
170
175
|
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Handle the search Indexer.
|
|
174
|
-
*/
|
|
175
|
-
class Search implements IWebSearch {
|
|
176
|
-
public readonly index: lunrMutable.index;
|
|
177
|
-
// @ts-ignore
|
|
178
|
-
public storage: Storage;
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Constructor.
|
|
182
|
-
*/
|
|
183
|
-
public constructor() {
|
|
184
|
-
this.index = lunrMutable(function (): void {
|
|
185
|
-
// FIXME: there is no types for this library
|
|
186
|
-
/* eslint no-invalid-this:off */
|
|
187
|
-
// @ts-ignore
|
|
188
|
-
this.field('name', { boost: 10 });
|
|
189
|
-
// @ts-ignore
|
|
190
|
-
this.field('description', { boost: 4 });
|
|
191
|
-
// @ts-ignore
|
|
192
|
-
this.field('author', { boost: 6 });
|
|
193
|
-
// @ts-ignore
|
|
194
|
-
this.field('keywords', { boost: 7 });
|
|
195
|
-
// @ts-ignore
|
|
196
|
-
this.field('version');
|
|
197
|
-
// @ts-ignore
|
|
198
|
-
this.field('readme');
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
this.index.builder.pipeline.remove(lunr.stemmer);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
public init() {
|
|
205
|
-
return Promise.resolve();
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Performs a query to the indexer.
|
|
210
|
-
* If the keyword is a * it returns all local elements
|
|
211
|
-
* otherwise performs a search
|
|
212
|
-
* @param {*} q the keyword
|
|
213
|
-
* @return {Array} list of results.
|
|
214
|
-
*/
|
|
215
|
-
public query(query: string): ISearchResult[] {
|
|
216
|
-
const localStorage = this.storage.localStorage as LocalStorage;
|
|
217
|
-
|
|
218
|
-
return query === '*'
|
|
219
|
-
? (localStorage.storagePlugin as any).get((items): any => {
|
|
220
|
-
items.map(function (pkg): any {
|
|
221
|
-
return { ref: pkg, score: 1 };
|
|
222
|
-
});
|
|
223
|
-
})
|
|
224
|
-
: this.index.search(`*${query}*`);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Add a new element to index
|
|
229
|
-
* @param {*} pkg the package
|
|
230
|
-
*/
|
|
231
|
-
public add(pkg: Version): void {
|
|
232
|
-
this.index.add({
|
|
233
|
-
id: pkg.name,
|
|
234
|
-
name: pkg.name,
|
|
235
|
-
description: pkg.description,
|
|
236
|
-
version: `v${pkg.version}`,
|
|
237
|
-
keywords: pkg.keywords,
|
|
238
|
-
author: pkg._npmUser ? pkg._npmUser.name : '???',
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Remove an element from the index.
|
|
244
|
-
* @param {*} name the id element
|
|
245
|
-
*/
|
|
246
|
-
public remove(name: string): void {
|
|
247
|
-
this.index.remove({ id: name });
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Force a re-index.
|
|
252
|
-
*/
|
|
253
|
-
public reindex(): void {
|
|
254
|
-
this.storage.getLocalDatabase((error, packages): void => {
|
|
255
|
-
if (error) {
|
|
256
|
-
// that function shouldn't produce any
|
|
257
|
-
throw error;
|
|
258
|
-
}
|
|
259
|
-
let i = packages.length;
|
|
260
|
-
while (i--) {
|
|
261
|
-
this.add(packages[i]);
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Set up the {Storage}
|
|
268
|
-
* @param {*} storage An storage reference.
|
|
269
|
-
*/
|
|
270
|
-
public configureStorage(storage: Storage): void {
|
|
271
|
-
this.storage = storage;
|
|
272
|
-
this.reindex();
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const SearchInstance = new Search();
|
|
277
|
-
|
|
278
|
-
export { SearchInstance };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
|
|
3
|
+
import { Package } from '@verdaccio/types';
|
|
4
|
+
|
|
5
|
+
import { Users } from '.';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Check whether the package metadta has enough data to be published
|
|
9
|
+
* @param pkg metadata
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export function isPublishablePackage(pkg: Package): boolean {
|
|
13
|
+
const keys: string[] = Object.keys(pkg);
|
|
14
|
+
|
|
15
|
+
return _.includes(keys, 'versions');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function isRelatedToDeprecation(pkgInfo: Package): boolean {
|
|
19
|
+
const { versions } = pkgInfo;
|
|
20
|
+
for (const version in versions) {
|
|
21
|
+
if (Object.prototype.hasOwnProperty.call(versions[version], 'deprecated')) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function validateInputs(localUsers: Users, username: string, isStar: boolean): boolean {
|
|
29
|
+
const isExistlocalUsers = _.isNil(localUsers[username]) === false;
|
|
30
|
+
if (isStar && isExistlocalUsers && localUsers[username]) {
|
|
31
|
+
return true;
|
|
32
|
+
} else if (!isStar && isExistlocalUsers) {
|
|
33
|
+
return false;
|
|
34
|
+
} else if (!isStar && !isExistlocalUsers) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
package/src/storage-utils.ts
CHANGED
|
@@ -3,11 +3,10 @@ import semver from 'semver';
|
|
|
3
3
|
|
|
4
4
|
import { errorUtils, pkgUtils, validatioUtils } from '@verdaccio/core';
|
|
5
5
|
import { API_ERROR, DIST_TAGS, HTTP_STATUS, USERS } from '@verdaccio/core';
|
|
6
|
-
import { Package, StringValue, Version } from '@verdaccio/types';
|
|
7
|
-
import { generateRandomHexString, isNil, normalizeDistTags } from '@verdaccio/utils';
|
|
6
|
+
import { AttachMents, Package, StringValue, Version, Versions } from '@verdaccio/types';
|
|
7
|
+
import { generateRandomHexString, isNil, isObject, normalizeDistTags } from '@verdaccio/utils';
|
|
8
8
|
|
|
9
9
|
import { LocalStorage } from './local-storage';
|
|
10
|
-
import { SearchInstance } from './search';
|
|
11
10
|
|
|
12
11
|
export const STORAGE = {
|
|
13
12
|
PACKAGE_FILE_NAME: 'package.json',
|
|
@@ -148,11 +147,9 @@ export function publishPackage(
|
|
|
148
147
|
localStorage: LocalStorage
|
|
149
148
|
): Promise<any> {
|
|
150
149
|
return new Promise<void>((resolve, reject): void => {
|
|
151
|
-
localStorage.addPackage(name, metadata, (err
|
|
150
|
+
localStorage.addPackage(name, metadata, (err): void => {
|
|
152
151
|
if (!_.isNull(err)) {
|
|
153
152
|
return reject(err);
|
|
154
|
-
} else if (!_.isUndefined(latest)) {
|
|
155
|
-
SearchInstance.add(latest);
|
|
156
153
|
}
|
|
157
154
|
return resolve();
|
|
158
155
|
});
|
|
@@ -246,3 +243,21 @@ export function tagVersion(data: Package, version: string, tag: StringValue): bo
|
|
|
246
243
|
}
|
|
247
244
|
return false;
|
|
248
245
|
}
|
|
246
|
+
|
|
247
|
+
export function isDifferentThanOne(versions: Versions | AttachMents): boolean {
|
|
248
|
+
return Object.keys(versions).length !== 1;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function hasInvalidPublishBody(manifest: Pick<Package, '_attachments' | 'versions'>) {
|
|
252
|
+
if (!manifest) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const { _attachments, versions } = manifest;
|
|
257
|
+
const res =
|
|
258
|
+
isObject(_attachments) === false ||
|
|
259
|
+
isDifferentThanOne(_attachments) ||
|
|
260
|
+
isObject(versions) === false ||
|
|
261
|
+
isDifferentThanOne(versions);
|
|
262
|
+
return res;
|
|
263
|
+
}
|