@verdaccio/search 7.0.0-next.2 → 8.0.0-next-8.1
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 +96 -702
- package/LICENSE +1 -1
- package/build/index.d.ts +2 -1
- package/build/index.js +26 -0
- package/build/index.js.map +1 -0
- package/build/search-utils.d.ts +2 -0
- package/build/search-utils.js +20 -0
- package/build/search-utils.js.map +1 -0
- package/build/search.d.ts +21 -0
- package/build/search.js +104 -0
- package/build/search.js.map +1 -0
- package/package.json +18 -9
- package/src/index.ts +2 -1
- package/src/search-utils.ts +15 -0
- package/src/search.ts +104 -0
- package/test/partials/search.json +273 -0
- package/test/search.test.ts +103 -0
- package/tsconfig.json +0 -6
- package/README.md +0 -29
- package/build/dist.js +0 -3965
- package/build/indexer.d.ts +0 -39
- package/jest.config.js +0 -12
- package/src/indexer.ts +0 -128
- package/test/index.spec.ts +0 -47
- package/types/patch.d.ts +0 -10
package/LICENSE
CHANGED
package/build/index.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { Search as default } from './search';
|
|
2
|
+
export * from './search-utils';
|
package/build/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
var _exportNames = {};
|
|
7
|
+
Object.defineProperty(exports, "default", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () {
|
|
10
|
+
return _search.Search;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
var _search = require("./search");
|
|
14
|
+
var _searchUtils = require("./search-utils");
|
|
15
|
+
Object.keys(_searchUtils).forEach(function (key) {
|
|
16
|
+
if (key === "default" || key === "__esModule") return;
|
|
17
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
18
|
+
if (key in exports && exports[key] === _searchUtils[key]) return;
|
|
19
|
+
Object.defineProperty(exports, key, {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () {
|
|
22
|
+
return _searchUtils[key];
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["_search","require","_searchUtils","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get"],"sources":["../src/index.ts"],"sourcesContent":["export { Search as default } from './search';\nexport * from './search-utils';\n"],"mappings":";;;;;;;;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAAAE,MAAA,CAAAC,IAAA,CAAAF,YAAA,EAAAG,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAJ,YAAA,CAAAI,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAZ,YAAA,CAAAI,GAAA;IAAA;EAAA;AAAA","ignoreList":[]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.removeDuplicates = removeDuplicates;
|
|
7
|
+
var _lodash = require("lodash");
|
|
8
|
+
function removeDuplicates(results) {
|
|
9
|
+
const pkgNames = [];
|
|
10
|
+
const orderByResults = (0, _lodash.orderBy)(results, ['verdaccioPrivate', 'asc']);
|
|
11
|
+
return orderByResults.filter(pkg => {
|
|
12
|
+
var _pkg$package, _pkg$package2;
|
|
13
|
+
if (pkgNames.includes(pkg === null || pkg === void 0 || (_pkg$package = pkg.package) === null || _pkg$package === void 0 ? void 0 : _pkg$package.name)) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
pkgNames.push(pkg === null || pkg === void 0 || (_pkg$package2 = pkg.package) === null || _pkg$package2 === void 0 ? void 0 : _pkg$package2.name);
|
|
17
|
+
return true;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=search-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-utils.js","names":["_lodash","require","removeDuplicates","results","pkgNames","orderByResults","orderBy","filter","pkg","_pkg$package","_pkg$package2","includes","package","name","push"],"sources":["../src/search-utils.ts"],"sourcesContent":["import { orderBy } from 'lodash';\n\nimport { searchUtils } from '@verdaccio/core';\n\nexport function removeDuplicates(results: searchUtils.SearchPackageItem[]) {\n const pkgNames: any[] = [];\n const orderByResults = orderBy(results, ['verdaccioPrivate', 'asc']);\n return orderByResults.filter((pkg) => {\n if (pkgNames.includes(pkg?.package?.name)) {\n return false;\n }\n pkgNames.push(pkg?.package?.name);\n return true;\n });\n}\n"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAIO,SAASC,gBAAgBA,CAACC,OAAwC,EAAE;EACzE,MAAMC,QAAe,GAAG,EAAE;EAC1B,MAAMC,cAAc,GAAG,IAAAC,eAAO,EAACH,OAAO,EAAE,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;EACpE,OAAOE,cAAc,CAACE,MAAM,CAAEC,GAAG,IAAK;IAAA,IAAAC,YAAA,EAAAC,aAAA;IACpC,IAAIN,QAAQ,CAACO,QAAQ,CAACH,GAAG,aAAHA,GAAG,gBAAAC,YAAA,GAAHD,GAAG,CAAEI,OAAO,cAAAH,YAAA,uBAAZA,YAAA,CAAcI,IAAI,CAAC,EAAE;MACzC,OAAO,KAAK;IACd;IACAT,QAAQ,CAACU,IAAI,CAACN,GAAG,aAAHA,GAAG,gBAAAE,aAAA,GAAHF,GAAG,CAAEI,OAAO,cAAAF,aAAA,uBAAZA,aAAA,CAAcG,IAAI,CAAC;IACjC,OAAO,IAAI;EACb,CAAC,CAAC;AACJ","ignoreList":[]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { searchUtils } from '@verdaccio/core';
|
|
2
|
+
import { ProxyInstanceList, ProxySearchParams } from '@verdaccio/proxy';
|
|
3
|
+
import { Config, Logger } from '@verdaccio/types';
|
|
4
|
+
declare class Search {
|
|
5
|
+
readonly uplinks: ProxyInstanceList;
|
|
6
|
+
readonly logger: Logger;
|
|
7
|
+
constructor(config: Config, logger: Logger);
|
|
8
|
+
private getProxyList;
|
|
9
|
+
/**
|
|
10
|
+
* Handle search on packages and proxies.
|
|
11
|
+
* Iterate all proxies configured and search in all endpoints in v2 and pipe all responses
|
|
12
|
+
* to a stream, once the proxies request has finished search in local storage for all packages
|
|
13
|
+
* (privated and cached).
|
|
14
|
+
*/
|
|
15
|
+
search(options: ProxySearchParams): Promise<searchUtils.SearchPackageItem[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Consume the upstream and pipe it to a transformable stream.
|
|
18
|
+
*/
|
|
19
|
+
private consumeSearchStream;
|
|
20
|
+
}
|
|
21
|
+
export { Search };
|
package/build/search.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.Search = void 0;
|
|
7
|
+
var _debug = _interopRequireDefault(require("debug"));
|
|
8
|
+
var _lodash = _interopRequireDefault(require("lodash"));
|
|
9
|
+
var _stream = require("stream");
|
|
10
|
+
var _proxy = require("@verdaccio/proxy");
|
|
11
|
+
var _searchUtils = require("./search-utils");
|
|
12
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
|
+
const debug = (0, _debug.default)('verdaccio:search');
|
|
14
|
+
class Search {
|
|
15
|
+
constructor(config, logger) {
|
|
16
|
+
this.logger = logger.child({
|
|
17
|
+
module: 'proxy'
|
|
18
|
+
});
|
|
19
|
+
this.uplinks = (0, _proxy.setupUpLinks)(config, this.logger);
|
|
20
|
+
}
|
|
21
|
+
getProxyList() {
|
|
22
|
+
const uplinksList = Object.keys(this.uplinks);
|
|
23
|
+
return uplinksList;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Handle search on packages and proxies.
|
|
28
|
+
* Iterate all proxies configured and search in all endpoints in v2 and pipe all responses
|
|
29
|
+
* to a stream, once the proxies request has finished search in local storage for all packages
|
|
30
|
+
* (privated and cached).
|
|
31
|
+
*/
|
|
32
|
+
async search(options) {
|
|
33
|
+
const results = [];
|
|
34
|
+
const upLinkList = this.getProxyList();
|
|
35
|
+
// const transformResults = new TransFormResults({ objectMode: true });
|
|
36
|
+
const streamPassThrough = new _stream.PassThrough({
|
|
37
|
+
objectMode: true
|
|
38
|
+
});
|
|
39
|
+
debug('uplinks found %s', upLinkList.length);
|
|
40
|
+
const searchUplinksStreams = upLinkList.map(uplinkId => {
|
|
41
|
+
const uplink = this.uplinks[uplinkId];
|
|
42
|
+
if (!uplink) {
|
|
43
|
+
// this line should never happens
|
|
44
|
+
this.logger.error({
|
|
45
|
+
uplinkId
|
|
46
|
+
}, 'uplink @upLinkId not found');
|
|
47
|
+
}
|
|
48
|
+
return this.consumeSearchStream(uplinkId, uplink, options, streamPassThrough);
|
|
49
|
+
});
|
|
50
|
+
try {
|
|
51
|
+
debug('searching on %s uplinks...', searchUplinksStreams === null || searchUplinksStreams === void 0 ? void 0 : searchUplinksStreams.length);
|
|
52
|
+
// only process those streams end successfully, if all request fails
|
|
53
|
+
// just include local storage results (if local fails then return 500)
|
|
54
|
+
await Promise.allSettled([...searchUplinksStreams]);
|
|
55
|
+
streamPassThrough.end();
|
|
56
|
+
for await (const chunk of streamPassThrough) {
|
|
57
|
+
if (_lodash.default.isArray(chunk)) {
|
|
58
|
+
chunk.filter(pkgItem => {
|
|
59
|
+
var _pkgItem$package;
|
|
60
|
+
debug(`streaming remote pkg name ${pkgItem === null || pkgItem === void 0 || (_pkgItem$package = pkgItem.package) === null || _pkgItem$package === void 0 ? void 0 : _pkgItem$package.name}`);
|
|
61
|
+
return true;
|
|
62
|
+
}).forEach(pkgItem => {
|
|
63
|
+
// @ts-ignore
|
|
64
|
+
return results.push({
|
|
65
|
+
...pkgItem,
|
|
66
|
+
verdaccioPkgCached: false,
|
|
67
|
+
verdaccioPrivate: false
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
debug('searching all uplinks done');
|
|
73
|
+
} catch (err) {
|
|
74
|
+
this.logger.error({
|
|
75
|
+
err: err === null || err === void 0 ? void 0 : err.message
|
|
76
|
+
}, ' error on uplinks search @{err}');
|
|
77
|
+
throw err;
|
|
78
|
+
}
|
|
79
|
+
return (0, _searchUtils.removeDuplicates)(results);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Consume the upstream and pipe it to a transformable stream.
|
|
84
|
+
*/
|
|
85
|
+
consumeSearchStream(uplinkId, uplink, options, searchPassThrough) {
|
|
86
|
+
return uplink.search({
|
|
87
|
+
...options
|
|
88
|
+
}).then(bodyStream => {
|
|
89
|
+
bodyStream.pipe(searchPassThrough, {
|
|
90
|
+
end: false
|
|
91
|
+
});
|
|
92
|
+
bodyStream.on('error', err => {
|
|
93
|
+
this.logger.error({
|
|
94
|
+
uplinkId,
|
|
95
|
+
err: err
|
|
96
|
+
}, 'search error for uplink @{uplinkId}: @{err?.message}');
|
|
97
|
+
searchPassThrough.end();
|
|
98
|
+
});
|
|
99
|
+
return new Promise(resolve => bodyStream.on('end', resolve));
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
exports.Search = Search;
|
|
104
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","names":["_debug","_interopRequireDefault","require","_lodash","_stream","_proxy","_searchUtils","e","__esModule","default","debug","buildDebug","Search","constructor","config","logger","child","module","uplinks","setupUpLinks","getProxyList","uplinksList","Object","keys","search","options","results","upLinkList","streamPassThrough","PassThrough","objectMode","length","searchUplinksStreams","map","uplinkId","uplink","error","consumeSearchStream","Promise","allSettled","end","chunk","_","isArray","filter","pkgItem","_pkgItem$package","package","name","forEach","push","verdaccioPkgCached","verdaccioPrivate","err","message","removeDuplicates","searchPassThrough","then","bodyStream","pipe","on","resolve","exports"],"sources":["../src/search.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport _ from 'lodash';\nimport { PassThrough } from 'stream';\n\nimport { searchUtils } from '@verdaccio/core';\nimport { IProxy, ProxyInstanceList, ProxySearchParams, setupUpLinks } from '@verdaccio/proxy';\nimport { Config, Logger } from '@verdaccio/types';\n\nimport { removeDuplicates } from './search-utils';\n\nconst debug = buildDebug('verdaccio:search');\n\nclass Search {\n public readonly uplinks: ProxyInstanceList;\n public readonly logger: Logger;\n constructor(config: Config, logger: Logger) {\n this.logger = logger.child({ module: 'proxy' });\n this.uplinks = setupUpLinks(config, this.logger);\n }\n\n private getProxyList() {\n const uplinksList = Object.keys(this.uplinks);\n\n return uplinksList;\n }\n\n /**\n * Handle search on packages and proxies.\n * Iterate all proxies configured and search in all endpoints in v2 and pipe all responses\n * to a stream, once the proxies request has finished search in local storage for all packages\n * (privated and cached).\n */\n public async search(options: ProxySearchParams): Promise<searchUtils.SearchPackageItem[]> {\n const results: searchUtils.SearchPackageItem[] = [];\n const upLinkList = this.getProxyList();\n // const transformResults = new TransFormResults({ objectMode: true });\n const streamPassThrough = new PassThrough({ objectMode: true });\n debug('uplinks found %s', upLinkList.length);\n const searchUplinksStreams = upLinkList.map((uplinkId: string) => {\n const uplink = this.uplinks[uplinkId];\n if (!uplink) {\n // this line should never happens\n this.logger.error({ uplinkId }, 'uplink @upLinkId not found');\n }\n return this.consumeSearchStream(uplinkId, uplink, options, streamPassThrough);\n });\n\n try {\n debug('searching on %s uplinks...', searchUplinksStreams?.length);\n // only process those streams end successfully, if all request fails\n // just include local storage results (if local fails then return 500)\n await Promise.allSettled([...searchUplinksStreams]);\n streamPassThrough.end();\n\n for await (const chunk of streamPassThrough) {\n if (_.isArray(chunk)) {\n (chunk as searchUtils.SearchItem[])\n .filter((pkgItem) => {\n debug(`streaming remote pkg name ${pkgItem?.package?.name}`);\n return true;\n })\n .forEach((pkgItem) => {\n // @ts-ignore\n return results.push({\n ...pkgItem,\n verdaccioPkgCached: false,\n verdaccioPrivate: false,\n });\n });\n }\n }\n debug('searching all uplinks done');\n } catch (err: any) {\n this.logger.error({ err: err?.message }, ' error on uplinks search @{err}');\n throw err;\n }\n\n return removeDuplicates(results);\n }\n\n /**\n * Consume the upstream and pipe it to a transformable stream.\n */\n private consumeSearchStream(\n uplinkId: string,\n uplink: IProxy,\n options: ProxySearchParams,\n searchPassThrough: PassThrough\n ): Promise<any> {\n return uplink.search({ ...options }).then((bodyStream) => {\n bodyStream.pipe(searchPassThrough, { end: false });\n bodyStream.on('error', (err: any): void => {\n this.logger.error(\n { uplinkId, err: err },\n 'search error for uplink @{uplinkId}: @{err?.message}'\n );\n searchPassThrough.end();\n });\n return new Promise((resolve) => bodyStream.on('end', resolve));\n });\n }\n}\n\nexport { Search };\n"],"mappings":"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","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@verdaccio/search",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "verdaccio search
|
|
5
|
-
"main": "./build/
|
|
3
|
+
"version": "8.0.0-next-8.1",
|
|
4
|
+
"description": "verdaccio search proxy",
|
|
5
|
+
"main": "./build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Juan Picado",
|
|
@@ -26,13 +26,21 @@
|
|
|
26
26
|
"verdaccio"
|
|
27
27
|
],
|
|
28
28
|
"engines": {
|
|
29
|
-
"node": ">=
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"debug": "4.3.7",
|
|
33
|
+
"lodash": "4.17.21",
|
|
34
|
+
"@verdaccio/config": "8.0.0-next-8.1",
|
|
35
|
+
"@verdaccio/core": "8.0.0-next-8.1",
|
|
36
|
+
"@verdaccio/logger": "8.0.0-next-8.1",
|
|
37
|
+
"@verdaccio/proxy": "8.0.0-next-8.1"
|
|
30
38
|
},
|
|
31
39
|
"devDependencies": {
|
|
32
|
-
"@verdaccio/types": "
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
40
|
+
"@verdaccio/types": "13.0.0-next-8.0",
|
|
41
|
+
"mockdate": "3.0.5",
|
|
42
|
+
"nock": "13.5.1",
|
|
43
|
+
"node-mocks-http": "1.14.1"
|
|
36
44
|
},
|
|
37
45
|
"funding": {
|
|
38
46
|
"type": "opencollective",
|
|
@@ -44,6 +52,7 @@
|
|
|
44
52
|
"type-check": "tsc --noEmit -p tsconfig.build.json",
|
|
45
53
|
"build:types": "tsc --emitDeclarationOnly -p tsconfig.build.json",
|
|
46
54
|
"build:js": "babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps",
|
|
47
|
-
"
|
|
55
|
+
"watch": "pnpm build:js -- --watch",
|
|
56
|
+
"build": "pnpm run build:js && pnpm run build:types"
|
|
48
57
|
}
|
|
49
58
|
}
|
package/src/index.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { Search as default } from './search';
|
|
2
|
+
export * from './search-utils';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { orderBy } from 'lodash';
|
|
2
|
+
|
|
3
|
+
import { searchUtils } from '@verdaccio/core';
|
|
4
|
+
|
|
5
|
+
export function removeDuplicates(results: searchUtils.SearchPackageItem[]) {
|
|
6
|
+
const pkgNames: any[] = [];
|
|
7
|
+
const orderByResults = orderBy(results, ['verdaccioPrivate', 'asc']);
|
|
8
|
+
return orderByResults.filter((pkg) => {
|
|
9
|
+
if (pkgNames.includes(pkg?.package?.name)) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
pkgNames.push(pkg?.package?.name);
|
|
13
|
+
return true;
|
|
14
|
+
});
|
|
15
|
+
}
|
package/src/search.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import buildDebug from 'debug';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import { PassThrough } from 'stream';
|
|
4
|
+
|
|
5
|
+
import { searchUtils } from '@verdaccio/core';
|
|
6
|
+
import { IProxy, ProxyInstanceList, ProxySearchParams, setupUpLinks } from '@verdaccio/proxy';
|
|
7
|
+
import { Config, Logger } from '@verdaccio/types';
|
|
8
|
+
|
|
9
|
+
import { removeDuplicates } from './search-utils';
|
|
10
|
+
|
|
11
|
+
const debug = buildDebug('verdaccio:search');
|
|
12
|
+
|
|
13
|
+
class Search {
|
|
14
|
+
public readonly uplinks: ProxyInstanceList;
|
|
15
|
+
public readonly logger: Logger;
|
|
16
|
+
constructor(config: Config, logger: Logger) {
|
|
17
|
+
this.logger = logger.child({ module: 'proxy' });
|
|
18
|
+
this.uplinks = setupUpLinks(config, this.logger);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private getProxyList() {
|
|
22
|
+
const uplinksList = Object.keys(this.uplinks);
|
|
23
|
+
|
|
24
|
+
return uplinksList;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Handle search on packages and proxies.
|
|
29
|
+
* Iterate all proxies configured and search in all endpoints in v2 and pipe all responses
|
|
30
|
+
* to a stream, once the proxies request has finished search in local storage for all packages
|
|
31
|
+
* (privated and cached).
|
|
32
|
+
*/
|
|
33
|
+
public async search(options: ProxySearchParams): Promise<searchUtils.SearchPackageItem[]> {
|
|
34
|
+
const results: searchUtils.SearchPackageItem[] = [];
|
|
35
|
+
const upLinkList = this.getProxyList();
|
|
36
|
+
// const transformResults = new TransFormResults({ objectMode: true });
|
|
37
|
+
const streamPassThrough = new PassThrough({ objectMode: true });
|
|
38
|
+
debug('uplinks found %s', upLinkList.length);
|
|
39
|
+
const searchUplinksStreams = upLinkList.map((uplinkId: string) => {
|
|
40
|
+
const uplink = this.uplinks[uplinkId];
|
|
41
|
+
if (!uplink) {
|
|
42
|
+
// this line should never happens
|
|
43
|
+
this.logger.error({ uplinkId }, 'uplink @upLinkId not found');
|
|
44
|
+
}
|
|
45
|
+
return this.consumeSearchStream(uplinkId, uplink, options, streamPassThrough);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
debug('searching on %s uplinks...', searchUplinksStreams?.length);
|
|
50
|
+
// only process those streams end successfully, if all request fails
|
|
51
|
+
// just include local storage results (if local fails then return 500)
|
|
52
|
+
await Promise.allSettled([...searchUplinksStreams]);
|
|
53
|
+
streamPassThrough.end();
|
|
54
|
+
|
|
55
|
+
for await (const chunk of streamPassThrough) {
|
|
56
|
+
if (_.isArray(chunk)) {
|
|
57
|
+
(chunk as searchUtils.SearchItem[])
|
|
58
|
+
.filter((pkgItem) => {
|
|
59
|
+
debug(`streaming remote pkg name ${pkgItem?.package?.name}`);
|
|
60
|
+
return true;
|
|
61
|
+
})
|
|
62
|
+
.forEach((pkgItem) => {
|
|
63
|
+
// @ts-ignore
|
|
64
|
+
return results.push({
|
|
65
|
+
...pkgItem,
|
|
66
|
+
verdaccioPkgCached: false,
|
|
67
|
+
verdaccioPrivate: false,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
debug('searching all uplinks done');
|
|
73
|
+
} catch (err: any) {
|
|
74
|
+
this.logger.error({ err: err?.message }, ' error on uplinks search @{err}');
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return removeDuplicates(results);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Consume the upstream and pipe it to a transformable stream.
|
|
83
|
+
*/
|
|
84
|
+
private consumeSearchStream(
|
|
85
|
+
uplinkId: string,
|
|
86
|
+
uplink: IProxy,
|
|
87
|
+
options: ProxySearchParams,
|
|
88
|
+
searchPassThrough: PassThrough
|
|
89
|
+
): Promise<any> {
|
|
90
|
+
return uplink.search({ ...options }).then((bodyStream) => {
|
|
91
|
+
bodyStream.pipe(searchPassThrough, { end: false });
|
|
92
|
+
bodyStream.on('error', (err: any): void => {
|
|
93
|
+
this.logger.error(
|
|
94
|
+
{ uplinkId, err: err },
|
|
95
|
+
'search error for uplink @{uplinkId}: @{err?.message}'
|
|
96
|
+
);
|
|
97
|
+
searchPassThrough.end();
|
|
98
|
+
});
|
|
99
|
+
return new Promise((resolve) => bodyStream.on('end', resolve));
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export { Search };
|